/* -*- Mode: C; tab-width: 4 -*-
* swirl --- swirly color-cycling patterns.
*/
#if 0
static const char sccsid[] = "@(#)swirl.c 4.00 97/01/01 xlockmore";
#endif
/* Copyright (c) 1994 M.Dobie <mrd@ecs.soton.ac.uk>
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* This file is provided AS IS with no warranties of any kind. The author
* shall have no liability with respect to the infringement of copyrights,
* trade secrets or any patents by this file or any part thereof. In no
* event will the author be liable for any lost revenue or profits or
* other special, indirect and consequential damages.
*
* 09-Oct-2016: dmo2118@gmail.com: Updated for new xshm.c.
* 13-May-1997: jwz@jwz.org: turned into a standalone program.
* 21-Apr-1995: improved startup time for TrueColour displays
* (limited to 16bpp to save memory) S.Early <sde1000@cam.ac.uk>
* 09-Jan-1995: fixed colour maps (more colourful) and the image now spirals
* outwards from the centre with a fixed number of points drawn
* every iteration. Thanks to M.Dobie <mrd@ecs.soton.ac.uk>.
* 1994: written. Copyright (c) 1994 M.Dobie <mrd@ecs.soton.ac.uk>
* based on original code by R.Taylor
*/
#ifdef STANDALONE
# define DEFAULTS "*count: 5 \n" \
"*delay: 10000 \n" \
"*ncolors: 200 \n" \
"*useSHM: True \n" \
"*fpsSolid: true \n" \
"*ignoreRotation: True \n" \
".lowrez: True \n" \
# define SMOOTH_COLORS
# define WRITABLE_COLORS
# define release_swirl 0
# define reshape_swirl 0
# define swirl_handle_event 0
# include "xlockmore.h" /* from the xscreensaver distribution */
# include "xshm.h"
#else /* !STANDALONE */
# include "xlock.h" /* from the xlockmore distribution */
# undef HAVE_XSHM_EXTENSION
#endif /* !STANDALONE */
ENTRYPOINT ModeSpecOpt swirl_opts = {
0, NULL, 0, NULL, NULL };
#include <time.h>
/****************************************************************/
#define MASS 4 /* maximum mass of a knot */
#define MIN_RES 5 /* minimim resolution (>= MIN_RES) */
#define MAX_RES 1 /* maximum resolution (>0) */
#define TWO_PLANE_PCNT 30 /* probability for two plane mode (0-100) */
#define RESTART 2500 /* number of cycles before restart */
#define BATCH_DRAW 100 /* points to draw per iteration */
/* knot types */
typedef enum {
NONE = 0,
ORBIT = (1 << 0),
WHEEL = (1 << 1),
PICASSO = (1 << 2),
RAY = (1 << 3),
HOOK = (1 << 4),
ALL = (1 << 5)
} KNOT_T;
/* a knot */
typedef struct Knot {
int x, y; /* position */
int m; /* mass */
KNOT_T t; /* type in the first (or only) plane */
KNOT_T T; /* type in second plane if there is one */
int M; /* mass in second plane if there is one */
} KNOT , *KNOT_P;
/* a colour specification */
typedef struct Colour {
unsigned short r, g, b;
} COLOUR , *COLOUR_P;
/* drawing direction */
typedef enum {
DRAW_RIGHT, DRAW_DOWN, DRAW_LEFT, DRAW_UP
} DIR_T;
/****************************************************************/
/* data associated with a swirl window */
typedef struct swirl_data {
/* window paramaters */
Window win; /* the window */
int width, height; /* window size */
int depth; /* depth */
int rdepth; /* real depth (for XImage) */
Visual *visual; /* visual */
/* swirl drawing parameters */
int n_knots; /* number of knots */
KNOT_P knots; /* knot details */
KNOT_T knot_type; /* general type of knots */
int resolution; /* drawing resolution, 1..5 */
int max_resolution; /* maximum resolution, MAX_RES */
int r; /* pixel step */
Bool two_plane; /* two plane mode? */
Bool first_plane; /* doing first plane? */
int start_again; /* when to restart */
/* spiral drawing parameters */
int x, y; /* current point */
DIR_T direction; /* current direction */
int dir_todo, dir_done; /* how many points in current direction? */
int batch_todo, batch_done; /* how many points in this batch */
Bool started, drawing; /* are we drawing? */
/* image stuff */
unsigned char *image; /* image data */
XImage *ximage;
XShmSegmentInfo shm_info;
/* colours stuff */
int colours; /* how many colours possible */
int dcolours; /* how many colours for shading */
#ifndef STANDALONE
Bool fixed_colourmap; /* fixed colourmap? */
#endif /* !STANDALONE */
Bool monochrome; /* monochrome? */
Colormap cmap; /* colour map for the window */
XColor *rgb_values; /* colour definitions array */
#ifndef STANDALONE
int current_map; /* current colour map, 0..dcolours-1 */
unsigned long fg, bg, white, black; /* black and white pixel values */
int shift; /* colourmap shift */
int dshift; /* colourmap shift while drawing */
XColor fgcol, bgcol; /* foreground and background colour specs */
#endif /* !STANDALONE */
Bool off_screen;
} SWIRL , *SWIRL_P;
#define SWIRLCOLOURS 13
#ifndef STANDALONE
/* basic colours */
static COLOUR basic_colours[SWIRLCOLOURS];
#endif /* !STANDALONE */
/* an array of swirls for each screen */
static SWIRL_P swirls = NULL;
/*
random_no
Return a random integer between 0 and n inclusive
- n is the maximum number
Returns a random integer */
static int
random_no(unsigned int n)
{
return ((int) ((n + 1) * (double) LRAND() / MAXRAND));
}
/****************************************************************/
/*
initialise_swirl
Initialise all the swirl data
- swirl is the swirl data */
static void
initialise_swirl(ModeInfo * mi, SWIRL_P swirl)
{
#ifndef STANDALONE
Display *display = MI_DISPLAY(mi);
#endif /* !STANDALONE */
swirl->width = 0; /* width and height of window */
swirl->height = 0;
swirl->depth = 1;
swirl->rdepth = 1;
swirl->visual = NULL;
swirl->resolution = MIN_RES + 1; /* current resolution */
swirl->max_resolution = MAX_RES; /* maximum resolution */
swirl->n_knots = 0; /* number of knots */
swirl->knot_type = ALL; /* general type of knots */
swirl->two_plane = False; /* two plane mode? */
swirl->first_plane = False; /* doing first plane? */
swirl->start_again = -1; /* restart counter */
/* drawing parameters */
swirl->x = 0;
swirl->y = 0;
swirl->started = False;
swirl->drawing = False;
/* image stuff */
swirl->image = NULL; /* image data */
swirl->ximage = NULL;
/* colours stuff */
swirl->colours = 0; /* how many colours possible */
swirl->dcolours = 0; /* how many colours for shading */
swirl->cmap = (Colormap) NULL;
swirl->rgb_values = NULL; /* colour definitions array */
#ifndef STANDALONE
swirl->current_map = 0; /* current colour map, 0..dcolours-1 */
/* set up fg fb colour specs */
swirl->white = MI_WIN_WHITE_PIXEL(mi);
swirl->black = MI_WIN_BLACK_PIXEL(mi);
#endif /* !STANDALONE */
#ifndef STANDALONE
swirl->fg = MI_FG_COLOR(mi);
swirl->bg = MI_BG_COLOR(mi);
swirl->fgcol.pixel = swirl->fg;
swirl->bgcol.pixel = swirl->bg;
XQueryColor(display, MI_COLORMAP(mi), &(swirl->fgcol));
XQueryColor(display, MI_COLORMAP(mi), &(swirl->bgcol));
#endif /* !STANDALONE */
}
/****************************************************************/
/*
* initialise_image
*
* Initialise the image for drawing to
*
* - swirl is the swirl data
*/
static void
initialise_image(ModeInfo * mi, SWIRL_P swirl)
{
Display *dpy = MI_DISPLAY(mi);
if (swirl->ximage != NULL)
destroy_xshm_image(dpy, swirl->ximage, &swirl->shm_info);
swirl->ximage = create_xshm_image(dpy, swirl->visual, swirl->rdepth,
ZPixmap, &swirl->shm_info,
swirl->width, swirl->height);
}
/****************************************************************/
#ifndef STANDALONE
/*
* initialise_colours
*
* Initialise the list of colours from which the colourmaps are derived
*
* - colours is the array to initialise
* - saturation is the saturation value to use 0->grey,
* 1.0->full saturation
*/
static void
initialise_colours(COLOUR * colours, float saturate)
{
int i;
/* start off fully saturated, medium and bright colours */
colours[0].r = 0xA000;
colours[0].g = 0x0000;
colours[0].b = 0x0000;
colours[1].r = 0xD000;
colours[1].g = 0x0000;
colours[1].b = 0x0000;
colours[2].r = 0x0000;
colours[2].g = 0x6000;
colours[2].b = 0x0000;
colours[3].r = 0x0000;
colours[3].g = 0x9000;
colours[3].b = 0x0000;
colours[4].r = 0x0000;
colours[4].g = 0x0000;
colours[4].b = 0xC000;
colours[5].r = 0x0000;
colours[5].g = 0x0000;
colours[5].b = 0xF000;
colours[6].r = 0xA000;
colours[6].g = 0x6000;
colours[6].b = 0x0000;
colours[7].r = 0xD000;
colours[7].g = 0x9000;
colours[7].b = 0x0000;
colours[8].r = 0xA000;
colours[8].g = 0x0000;
colours[8].b = 0xC000;
colours[9].r = 0xD000;
colours[9].g = 0x0000;
colours[9].b = 0xF000;
colours[10].r = 0x0000;
colours[10].g = 0x6000;
colours[10].b = 0xC000;
colours[11].r = 0x0000;
colours[11].g = 0x9000;
colours[11].b = 0xF000;
colours[12].r = 0xA000;
colours[12].g = 0xA000;
colours[12].b = 0xA000;
/* add white for low saturation */
for (i = 0; i < SWIRLCOLOURS - 1; i++) {
unsigned short max_rg, max;
/* what is the max intensity for this colour? */
max_rg = (colours[i].r > colours[i].g) ? colours[i].r : colours[i].g;
max = (max_rg > colours[i].b) ? max_rg : colours[i].b;
/* bring elements up to max as saturation approaches 0.0 */
colours[i].r += (unsigned short) ((float) (1.0 - saturate) *
((float) max - colours[i].r));
colours[i].g += (unsigned short) ((float) (1.0 - saturate) *
((float) max - colours[i].g));
colours[i].b += (unsigned short) ((float) (1.0 - saturate) *
((float) max - colours[i].b));
}
}
#endif /* !STANDALONE */
/****************************************************************/
#ifndef STANDALONE
/*
* set_black_and_white
*
* Set the entries for foreground & background pixels and
* WhitePixel & BlackPixel in an array of colour specifications.
*
* - swirl is the swirl data
* - values is the array of specifications
*/
static void
set_black_and_white(SWIRL_P swirl, XColor * values)
{
unsigned long white, black;
/* where is black and white? */
white = swirl->white;
black = swirl->black;
/* set black and white up */
values[white].flags = DoRed | DoGreen | DoBlue;
values[white].pixel = white;
values[white].red = 0xFFFF;
values[white].green = 0xFFFF;
values[white].blue = 0xFFFF;
values[black].flags = DoRed | DoGreen | DoBlue;
values[black].pixel = black;
values[black].red = 0;
values[black].green = 0;
values[black].blue = 0;
/* copy the colour specs from the original entries */
values[swirl->fg] = swirl->fgcol;
values[swirl->bg] = swirl->bgcol;
}
/****************************************************************/
/*
* set_colour
*
* Set an entry in an array of XColor specifications. The given entry will be
* set to the given colour. If the entry corresponds to the foreground,
* background, WhitePixel, or BlackPixel it is ignored and the given colour
* is is put in the next entry.
*
* Therefore, the given colour may be placed up to four places after the
* specified entry in the array, if foreground, background, white, or black
* intervene.
*
* - swirl is the swirl data
* - value points to a pointer to the array entry. It gets updated to
* point to the next free entry.
* - pixel points to the current pixel number. It gets updated.
* - c points to the colour to add
*/
static void
set_colour(SWIRL_P swirl, XColor ** value, unsigned long *pixel, COLOUR_P c)
{
Bool done;
unsigned long fg, bg, white, black;
/* where are foreground, background, white, and black? */
fg = swirl->fg;
bg = swirl->bg;
white = swirl->white;
black = swirl->black;
/* haven't set it yet */
done = False;
/* try and set the colour */
while (!done) {
(**value).flags = DoRed | DoGreen | DoBlue;
(**value).pixel = *pixel;
/* white, black, fg, bg, or a colour? */
if ((*pixel != fg) && (*pixel != bg) &&
(*pixel != white) && (*pixel != black)) {
(**value).red = c->r;
(**value).green = c->g;
(**value).blue = c->b;
/* now we've done it */
done = True;
}
/* next pixel */
(*value)++;
(*pixel)++;
}
}
/****************************************************************/
/*
* get_colour
*
* Get an entry from an array of XColor specifications. The next colour from
* the array will be returned. Foreground, background, WhitePixel, or
* BlackPixel will be ignored.
*
* - swirl is the swirl data
* - value points the array entry. It is updated to point to the entry
* following the one returned.
* - c is set to the colour found
*/
static void
get_colour(SWIRL_P swirl, XColor ** value, COLOUR_P c)
{
Bool done;
unsigned long fg, bg, white, black;
/* where is white and black? */
fg = swirl->fg;
bg = swirl->bg;
white = swirl->white;
black = swirl->black;
/* haven't set it yet */
done = False;
/* try and set the colour */
while (!done) {
/* black, white or a colour? */
if (((*value)->pixel != fg) && ((*value)->pixel != bg) &&
((*value)->pixel != white) && ((*value)->pixel != black)) {
c->r = (*value)->red;
c->g = (*value)->green;
c->b = (*value)->blue;
/* now we've done it */
done = True;
}
/* next value */
(*value)++;
}
}
#endif /* !STANDALONE */
/****************************************************************/
#ifndef STANDALONE
/*
* interpolate
*
* Generate n colours between c1 and c2. n XColors at *value are set up with
* ascending pixel values.
*
* If the pixel range includes BlackPixel or WhitePixel they are set to black
* and white respectively but otherwise ignored. Therefore, up to n+2 colours
* may actually be set by this function.
*
* - swirl is the swirl data
* - values points a pointer to an array of XColors to update
* - pixel points to the pixel number to start at
* - k n is the number of colours to generate
* - c1, c2 are the colours to interpolate between
*/
static void
interpolate(SWIRL_P swirl, XColor ** values, unsigned long *pixel, int n, COLOUR_P c1, COLOUR_P c2)
{
int i, r, g, b;
COLOUR c;
unsigned short maxv;
/* maximum value */
maxv = (255 << 8);
for (i = 0; i < n / 2 && (int) *pixel < swirl->colours; i++) {
/* work out the colour */
r = c1->r + 2 * i * ((int) c2->r) / n;
c.r = (r > (int) maxv) ? maxv : r;
g = c1->g + 2 * i * ((int) c2->g) / n;
c.g = (g > (int) maxv) ? maxv : g;
b = c1->b + 2 * i * ((int) c2->b) / n;
c.b = (b > (int) maxv) ? maxv : b;
/* set it up */
set_colour(swirl, values, pixel, &c);
}
for (i = n / 2; i >= 0 && (int) *pixel < swirl->colours; i--) {
r = c2->r + 2 * i * ((int) c1->r) / n;
c.r = (r > (int) maxv) ? maxv : r;
g = c2->g + 2 * i * ((int) c1->g) / n;
c.g = (g > (int) maxv) ? maxv : g;
b = c2->b + 2 * i * ((int) c1->b) / n;
c.b = (b > (int) maxv) ? maxv : b;
/* set it up */
set_colour(swirl, values, pixel, &c);
}
}
/****************************************************************/
/*
* basic_map
*
* Generate a `random' closed loop colourmap that occupies the whole colour
* map.
*
* - swirl is the swirl data
* - values is the array of colour definitions to set up
*/
static void
basic_map(SWIRL_P swirl, XColor * values)
{
COLOUR c[3];
int i;
unsigned short r1, g1, b1, r2, g2, b2, r3, g3, b3;
int L1, L2, L3, L;
unsigned long pixel;
XColor *value;
/* start at the beginning of the colour map */
pixel = 0;
value = values;
/* choose 3 different basic colours at random */
for (i = 0; i < 3;) {
int j;
Bool same;
/* choose colour i */
c[i] = basic_colours[random_no(SWIRLCOLOURS - 1)];
/* assume different */
same = False;
/* different from the rest? */
for (j = 0; j < i; j++)
if ((c[i].r == c[j].r) &&
(c[i].g == c[j].g) &&
(c[i].b == c[j].b))
same = True;
/* ready for the next colour? */
if (!same)
i++;
}
/* extract components into variables */
r1 = c[0].r;
g1 = c[0].g;
b1 = c[0].b;
r2 = c[1].r;
g2 = c[1].g;
b2 = c[1].b;
r3 = c[2].r;
g3 = c[2].g;
b3 = c[2].b;
/* work out the lengths of each side of the triangle */
L1 = (int) sqrt((((double) r1 - r2) * ((double) r1 - r2) +
((double) g1 - g2) * ((double) g1 - g2) +
((double) b1 - b2) * ((double) b1 - b2)));
L2 = (int) sqrt((((double) r3 - r2) * ((double) r3 - r2) +
((double) g3 - g2) * ((double) g3 - g2) +
((double) b3 - b2) * ((double) b3 - b2)));
L3 = (int) sqrt((((double) r1 - r3) * ((double) r1 - r3) +
((double) g1 - g3) * ((double) g1 - g3) +
((double) b1 - b3) * ((double) b1 - b3)));
L = L1 + L2 + L3;
/* allocate colours in proportion to the lengths of the sides */
interpolate(swirl, &value, &pixel,
(int) ((double) swirl->dcolours * ((double) L1 / (double) L)) + 1, c, c + 1);
interpolate(swirl, &value, &pixel,
(int) ((double) swirl->dcolours * ((double) L2 / (double) L)) + 1, c + 1, c + 2);
interpolate(swirl, &value, &pixel,
(int) ((double) swirl->dcolours * ((double) L3 / (double) L)) + 1, c + 2, c);
/* fill up any remaining slots (due to rounding) */
while ((int) pixel < swirl->colours) {
/* repeat the last colour */
set_colour(swirl, &value, &pixel, c);
}
/* ensure black and white are correct */
if (!swirl->fixed_colourmap)
set_black_and_white(swirl, values);
}
/****************************************************************/
/*
* pre_rotate
*
* Generate pre-rotated versions of the colour specifications
*
* - swirl is the swirl data
* - values is an array of colour specifications
*/
static void
pre_rotate(SWIRL_P swirl, XColor * values)
{
int i, j;
XColor *src, *dest;
int dcolours;
unsigned long pixel;
/* how many colours to display? */
dcolours = swirl->dcolours;
/* start at the first map */
src = values;
dest = values + swirl->colours;
/* generate dcolours-1 rotated maps */
for (i = 0; i < dcolours - 1; i++) {
COLOUR first;
/* start at the first pixel */
pixel = 0;
/* remember the first one and skip it */
get_colour(swirl, &src, &first);
/* put a rotated version of src at dest */
for (j = 0; j < dcolours - 1; j++) {
COLOUR c;
/* get the source colour */
get_colour(swirl, &src, &c);
/* set the colour */
set_colour(swirl, &dest, &pixel, &c);
}
/* put the first one at the end */
set_colour(swirl, &dest, &pixel, &first);
/* NB: src and dest should now be ready for the next table */
/* ensure black and white are properly set */
set_black_and_white(swirl, src);
}
}
/****************************************************************/
/*
* create_colourmap
*
* Create a read/write colourmap to use
*
* - swirl is the swirl data
*/
static void
create_colourmap(ModeInfo * mi, SWIRL_P swirl)
{
Display *display = MI_DISPLAY(mi);
int preserve;
int n_rotations;
int i;
Bool truecolor;
unsigned long redmask, greenmask, bluemask;
swirl->fixed_colourmap = !setupColormap(mi, &(swirl->colours),
&truecolor, &redmask, &greenmask, &bluemask);
preserve = preserveColors(swirl->fg, swirl->bg, swirl->white, swirl->black);
/* how many colours should we animate? */
swirl->dcolours = (swirl->colours > preserve + 1) ?
swirl->colours - preserve : swirl->colours;
if (MI_NPIXELS(mi) < 2)
return;
/* how fast to shift the colourmap? */
swirl->shift = (swirl->colours > 64) ? swirl->colours / 64 : 1;
swirl->dshift = (swirl->shift > 1) ? swirl->shift * 2 : 1;
/* how may colour map rotations are there? */
n_rotations = (swirl->fixed_colourmap) ? 1 : swirl->dcolours;
/* allocate space for colour definitions (if not already there) */
if (swirl->rgb_values == NULL) {
swirl->rgb_values = (XColor *) calloc((swirl->colours + 3) * n_rotations,
sizeof (XColor));
/* create a colour map */
if (!swirl->fixed_colourmap)
swirl->cmap =
XCreateColormap(display, swirl->win, swirl->visual, AllocAll);
}
/* select a set of colours for the colour map */
basic_map(swirl, swirl->rgb_values);
/* are we rotating them? */
if (!swirl->fixed_colourmap) {
/* generate rotations of the colour maps */
pre_rotate(swirl, swirl->rgb_values);
/* store the colours in the colour map */
XStoreColors(display, swirl->cmap, swirl->rgb_values, swirl->colours);
} else {
if (truecolor) {
int rsh, gsh, bsh;
unsigned long int t;
t = redmask;
for (i = 0; (int) t > 0; i++, t >>= 1);
rsh = 16 - i;
t = greenmask;
for (i = 0; (int) t > 0; i++, t >>= 1);
gsh = 16 - i;
t = bluemask;
for (i = 0; (int) t > 0; i++, t >>= 1);
bsh = 16 - i;
for (i = 0; i < swirl->colours; i++)
swirl->rgb_values[i].pixel =
((rsh > 0 ? (swirl->rgb_values[i].red) >> rsh :
(swirl->rgb_values[i].red) << (-rsh)) & redmask) |
((gsh > 0 ? (swirl->rgb_values[i].green) >> gsh :
(swirl->rgb_values[i].green) << (-gsh)) & greenmask) |
((bsh > 0 ? (swirl->rgb_values[i].blue) >> bsh :
(swirl->rgb_values[i].blue) << (-bsh)) & bluemask);
} else {
/* lookup the colours in the fixed colour map */
for (i = 0; i < swirl->colours; i++)
(void) XAllocColor(display, MI_COLORMAP(mi),
&(swirl->rgb_values[i]));
}
}
}
/****************************************************************/
/*
* install_map
*
* Install a new set of colours into the colour map
*
* - dpy is the display
* - swirl is the swirl data
* - shift is the amount to rotate the colour map by
*/
static void
install_map(Display * dpy, SWIRL_P swirl, int shift)
{
if (!swirl->fixed_colourmap) {
/* shift the colour map */
swirl->current_map = (swirl->current_map + shift) %
swirl->dcolours;
/* store it */
XStoreColors(dpy, swirl->cmap,
swirl->rgb_values +
swirl->current_map * swirl->colours,
swirl->colours);
}
}
#endif /* !STANDALONE */
/****************************************************************/
/*
* create_knots
*
* Initialise the array of knot
*
* swirl is the swirl data
*/
static void
create_knots(SWIRL_P swirl)
{
int k;
Bool orbit, wheel, picasso, ray, hook;
KNOT_P knot;
/* create array for knots */
if (swirl->knots)
(void) free((void *) swirl->knots);
swirl->knots = (KNOT_P) calloc(swirl->n_knots, sizeof (KNOT));
/* no knots yet */
orbit = wheel = picasso = ray = hook = False;
/* what types do we have? */
if ((int) swirl->knot_type & (int) ALL) {
orbit = wheel = ray = hook = True;
} else {
if ((int) swirl->knot_type & (int) ORBIT)
orbit = True;
if ((int) swirl->knot_type & (int) WHEEL)
wheel = True;
if ((int) swirl->knot_type & (int) PICASSO)
picasso = True;
if ((int) swirl->knot_type & (int) RAY)
ray = True;
if ((int) swirl->knot_type & (int) HOOK)
hook = True;
}
/* initialise each knot */
knot = swirl->knots;
for (k = 0; k < swirl->n_knots; k++) {
/* position */
knot->x = random_no((unsigned int) swirl->width);
knot->y = random_no((unsigned int) swirl->height);
/* mass */
knot->m = random_no(MASS) + 1;
/* can be negative */
if (random_no(100) > 50)
knot->m *= -1;
/* type */
knot->t = NONE;
while (knot->t == NONE) {
/* choose a random one from the types available */
switch (random_no(4)) {
case 0:
if (orbit)
knot->t = ORBIT;
break;
case 1:
if (wheel)
knot->t = WHEEL;
break;
case 2:
if (picasso)
knot->t = PICASSO;
break;
case 3:
if (ray)
knot->t = RAY;
break;
case 4:
if (hook)
knot->t = HOOK;
break;
}
}
/* if two planes, do same for second plane */
if (swirl->two_plane) {
knot->T = NONE;
while (knot->T == NONE || knot->T == knot->t) {
/* choose a different type */
switch (random_no(4)) {
case 0:
if (orbit)
knot->T = ORBIT;
break;
case 1:
if (wheel)
knot->T = WHEEL;
break;
case 2:
if (picasso)
knot->T = PICASSO;
break;
case 3:
if (ray)
knot->T = RAY;
break;
case 4:
if (hook)
knot->T = HOOK;
break;
}
}
}
/* next knot */
knot++;
}
}
/****************************************************************/
/*
* do_point
*
* Work out the pixel value at i, j. Ensure it does not clash with BlackPixel
* or WhitePixel.
*
* - swirl is the swirl data
* - i, j is the point to calculate
*
* Returns the value of the point
*/
static unsigned long
do_point(SWIRL_P swirl, int i, int j)
{
int tT, k, value, add;
double dx, dy, theta, dist;
int dcolours, qcolours;
double rads;
KNOT_P knot;
/* how many colours? */
dcolours = swirl->dcolours;
qcolours = dcolours / 4;
/* colour step round a circle */
rads = (double) dcolours / (2.0 * M_PI);
/* start at zero */
value = 0;
/* go through all the knots */
knot = swirl->knots;
for (k = 0; k < swirl->n_knots; k++) {
dx = i - knot->x;
dy = j - knot->y;
/* in two_plane mode get the appropriate knot type */
if (swirl->two_plane)
tT = (int) ((swirl->first_plane) ? knot->t : knot->T);
else
tT = (int) knot->t;
/* distance from knot */
dist = sqrt(dx * dx + dy * dy);
/* nothing to add at first */
add = 0;
/* work out the contribution (if close enough) */
if (dist > 0.1)
switch (tT) {
case ORBIT:
add = (int) (dcolours / (1.0 + 0.01 * abs(knot->m) * dist));
break;
case WHEEL:
/* Avoid atan2: DOMAIN error message */
if (dy == 0.0 && dx == 0.0)
theta = 1.0;
else
theta = (atan2(dy, dx) + M_PI) / M_PI;
if (theta < 1.0)
add = (int) (dcolours * theta +
sin(0.1 * knot->m * dist) *
qcolours * exp(-0.01 * dist));
else
add = (int) (dcolours * (theta - 1.0) +
sin(0.1 * knot->m * dist) *
qcolours * exp(-0.01 * dist));
break;
case PICASSO:
add = (int) (dcolours *
fabs(cos(0.002 * knot->m * dist)));
break;
case RAY:
/* Avoid atan2: DOMAIN error message */
if (dy == 0.0 && dx == 0.0)
add = 0;
else
add = (int) (dcolours * fabs(sin(2.0 * atan2(dy, dx))));
break;
case HOOK:
/* Avoid atan2: DOMAIN error message */
if (dy == 0.0 && dx == 0.0)
add = (int) (0.05 * (abs(knot->m) - 1) * dist);
else
add = (int) (rads * atan2(dy, dx) +
0.05 * (abs(knot->m) - 1) * dist);
break;
}
/* for a +ve mass add on the contribution else take it off */
if (knot->m > 0)
value += add;
else
value -= add;
/* next knot */
knot++;
}
/* toggle plane */
swirl->first_plane = (!swirl->first_plane);
/* make sure we handle -ve values properly */
if (value >= 0)
value = (value % dcolours) + 2;
else
value = dcolours - (abs(value) % (dcolours - 1));
#ifndef STANDALONE
/* if fg and bg are 1 and 0 we should be OK, but just in case */
while ((dcolours > 2) &&
(((value % swirl->colours) == (int) swirl->fg) ||
((value % swirl->colours) == (int) swirl->bg) ||
((value % swirl->colours) == (int) swirl->white) ||
((value % swirl->colours) == (int) swirl->black))) {
value++;
}
#endif /* !STANDALONE */
/* definitely make sure it is in range */
value = value % swirl->colours;
/* lookup the pixel value if necessary */
#ifndef STANDALONE
if (swirl->fixed_colourmap && swirl->dcolours > 2)
#endif
value = swirl->rgb_values[value].pixel;
/* return it */
return ((unsigned long) value);
}
/****************************************************************/
/*
* draw_block
*
* Draw a square block of points with the same value.
*
* - ximage is the XImage to draw on.
* - x, y is the top left corner
* - s is the length of each side
* - v is the value
*/
static void
draw_block(XImage * ximage, int x, int y, int s, unsigned long v)
{
int a, b;
for (a = 0; a < s; a++)
for (b = 0; b < s; b++) {
XPutPixel(ximage, x + b, y + a, v);
}
}
/****************************************************************/
/*
* draw_point Draw the current point in a swirl pattern onto the XImage
*
* - swirl is the swirl
* - win is the window to update
*/
static void
draw_point(ModeInfo * mi, SWIRL_P swirl)
{
int r;
int x, y;
/* get current point coordinates and resolution */
x = swirl->x;
y = swirl->y;
r = swirl->r;
/* check we are within the window */
if ((x < 0) || (x > swirl->width - r) || (y < 0) || (y > swirl->height - r))
return;
/* what style are we drawing? */
if (swirl->two_plane) {
int r2;
/* halve the block size */
r2 = r / 2;
/* interleave blocks at half r */
draw_block(swirl->ximage, x, y, r2, do_point(swirl, x, y));
draw_block(swirl->ximage, x + r2, y, r2, do_point(swirl, x + r2, y));
draw_block(swirl->ximage, x + r2, y + r2, r2, do_point(swirl,
x + r2, y + r2));
draw_block(swirl->ximage, x, y + r2, r2, do_point(swirl, x, y + r2));
} else
draw_block(swirl->ximage, x, y, r, do_point(swirl, x, y));
/* update the screen */
put_xshm_image(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), swirl->ximage,
x, y, x, y, r, r, &swirl->shm_info);
}
/****************************************************************/
/*
* next_point Move to the next point in the spiral pattern
* - swirl is the swirl
* - win is the window to update
*/
static void
next_point(SWIRL_P swirl)
{
/* more to do in this direction? */
if (swirl->dir_done < swirl->dir_todo) {
/* move in the current direction */
switch (swirl->direction) {
case DRAW_RIGHT:
swirl->x += swirl->r;
break;
case DRAW_DOWN:
swirl->y += swirl->r;
break;
case DRAW_LEFT:
swirl->x -= swirl->r;
break;
case DRAW_UP:
swirl->y -= swirl->r;
break;
}
/* done another point */
swirl->dir_done++;
} else {
/* none drawn yet */
swirl->dir_done = 0;
/* change direction - check and record if off screen */
switch (swirl->direction) {
case DRAW_RIGHT:
swirl->direction = DRAW_DOWN;
if (swirl->x > swirl->width - swirl->r) {
/* skip these points */
swirl->dir_done = swirl->dir_todo;
swirl->y += (swirl->dir_todo * swirl->r);
/* check for finish */
if (swirl->off_screen)
swirl->drawing = False;
swirl->off_screen = True;
} else
swirl->off_screen = False;
break;
case DRAW_DOWN:
swirl->direction = DRAW_LEFT;
swirl->dir_todo++;
if (swirl->y > swirl->height - swirl->r) {
/* skip these points */
swirl->dir_done = swirl->dir_todo;
swirl->x -= (swirl->dir_todo * swirl->r);
/* check for finish */
if (swirl->off_screen)
swirl->drawing = False;
swirl->off_screen = True;
} else
swirl->off_screen = False;
break;
case DRAW_LEFT:
swirl->direction = DRAW_UP;
if (swirl->x < 0) {
/* skip these points */
swirl->dir_done = swirl->dir_todo;
swirl->y -= (swirl->dir_todo * swirl->r);
/* check for finish */
if (swirl->off_screen)
swirl->drawing = False;
swirl->off_screen = True;
} else
swirl->off_screen = False;
break;
case DRAW_UP:
swirl->direction = DRAW_RIGHT;
swirl->dir_todo++;
if (swirl->y < 0) {
/* skip these points */
swirl->dir_done = swirl->dir_todo;
swirl->x += (swirl->dir_todo * swirl->r);
/* check for finish */
if (swirl->off_screen)
swirl->drawing = False;
swirl->off_screen = True;
} else
swirl->off_screen = False;
break;
}
}
}
/****************************************************************/
/*
* init_swirl
*
* Initialise things for swirling
*
* - win is the window to draw in
*/
ENTRYPOINT void
init_swirl(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
SWIRL_P swirl;
MI_INIT (mi, swirls);
swirl = &(swirls[MI_SCREEN(mi)]);
initialise_swirl(mi, swirl);
/* get window parameters */
swirl->win = window;
swirl->width = MI_WIN_WIDTH(mi);
swirl->height = MI_WIN_HEIGHT(mi);
swirl->depth = MI_WIN_DEPTH(mi);
swirl->rdepth = swirl->depth;
swirl->visual = MI_VISUAL(mi);
if (swirl->depth > 16)
swirl->depth = 16;
/* initialise image for speeding up drawing */
initialise_image(mi, swirl);
/* clear the window (before setting the colourmap) */
XClearWindow(display, MI_WINDOW(mi));
#ifdef STANDALONE
swirl->rgb_values = mi->colors;
swirl->colours = mi->npixels;
swirl->dcolours = swirl->colours;
/* swirl->fixed_colourmap = !mi->writable_p;*/
#else /* !STANDALONE */
/* initialise the colours from which the colourmap is derived */
initialise_colours(basic_colours, MI_SATURATION(mi));
/* set up the colour map */
create_colourmap(mi, swirl);
/* attach the colour map to the window (if we have one) */
if (!swirl->fixed_colourmap) {
#if 1
setColormap(display, window, swirl->cmap, MI_WIN_IS_INWINDOW(mi));
#else
XSetWindowColormap(display, window, swirl->cmap);
(void) XSetWMColormapWindows(display, window, &window, 1);
XInstallColormap(display, swirl->cmap);
#endif
}
#endif /* STANDALONE */
/* resolution starts off chunky */
swirl->resolution = MIN_RES + 1;
/* calculate the pixel step for this resulution */
swirl->r = (1 << (swirl->resolution - 1));
/* how many knots? */
swirl->n_knots = random_no((unsigned int) MI_BATCHCOUNT(mi) / 2) +
MI_BATCHCOUNT(mi) + 1;
/* what type of knots? */
swirl->knot_type = ALL; /* for now */
/* use two_plane mode occaisionally */
if (random_no(100) <= TWO_PLANE_PCNT) {
swirl->two_plane = swirl->first_plane = True;
swirl->max_resolution = 2;
} else
swirl->two_plane = False;
/* fix the knot values */
create_knots(swirl);
/* we are off */
swirl->started = True;
swirl->drawing = False;
}
/****************************************************************/
/*
* draw_swirl
*
* Draw one iteration of swirling
*
* - win is the window to draw in
*/
ENTRYPOINT void
draw_swirl(ModeInfo * mi)
{
SWIRL_P swirl = &(swirls[MI_SCREEN(mi)]);
/* are we going? */
if (swirl->started) {
/* in the middle of drawing? */
if (swirl->drawing) {
#ifdef STANDALONE
if (mi->writable_p)
rotate_colors(mi->xgwa.screen, MI_COLORMAP(mi),
swirl->rgb_values, swirl->colours, 1);
#else /* !STANDALONE */
/* rotate the colours */
install_map(MI_DISPLAY(mi), swirl, swirl->dshift);
#endif /* !STANDALONE */
/* draw a batch of points */
swirl->batch_todo = BATCH_DRAW;
while ((swirl->batch_todo > 0) && swirl->drawing) {
/* draw a point */
draw_point(mi, swirl);
/* move to the next point */
next_point(swirl);
/* done a point */
swirl->batch_todo--;
}
} else {
#ifdef STANDALONE
if (mi->writable_p)
rotate_colors(mi->xgwa.screen, MI_COLORMAP(mi),
swirl->rgb_values, swirl->colours, 1);
#else /* !STANDALONE */
/* rotate the colours */
install_map(MI_DISPLAY(mi), swirl, swirl->shift);
#endif /* !STANDALONE */
/* time for a higher resolution? */
if (swirl->resolution > swirl->max_resolution) {
/* move to higher resolution */
swirl->resolution--;
/* calculate the pixel step for this resulution */
swirl->r = (1 << (swirl->resolution - 1));
/* start drawing again */
swirl->drawing = True;
/* start in the middle of the screen */
swirl->x = (swirl->width - swirl->r) / 2;
swirl->y = (swirl->height - swirl->r) / 2;
/* initialise spiral drawing parameters */
swirl->direction = DRAW_RIGHT;
swirl->dir_todo = 1;
swirl->dir_done = 0;
} else {
/* all done, decide when to restart */
if (swirl->start_again == -1) {
/* start the counter */
swirl->start_again = RESTART;
} else if (swirl->start_again == 0) {
/* reset the counter */
swirl->start_again = -1;
#ifdef STANDALONE
/* Pick a new colormap! */
XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
free_colors (mi->xgwa.screen, MI_COLORMAP(mi),
mi->colors, mi->npixels);
make_smooth_colormap (mi->xgwa.screen, MI_VISUAL(mi),
MI_COLORMAP(mi),
mi->colors, &mi->npixels, True,
&mi->writable_p, True);
swirl->colours = mi->npixels;
#endif /* STANDALONE */
/* start again */
init_swirl(mi);
} else
/* decrement the counter */
swirl->start_again--;
}
}
}
}
/****************************************************************/
ENTRYPOINT void
free_swirl (ModeInfo * mi)
{
SWIRL_P swirl = &(swirls[MI_SCREEN(mi)]);
#ifndef STANDALONE
if (swirl->cmap != (Colormap) NULL)
XFreeColormap(MI_DISPLAY(mi), swirl->cmap);
#endif /* STANDALONE */
#ifndef STANDALONE
if (swirl->rgb_values != NULL)
XFree((void *) swirl->rgb_values);
#endif /* !STANDALONE */
if (swirl->ximage != NULL)
destroy_xshm_image(MI_DISPLAY(mi), swirl->ximage,
&swirl->shm_info);
if (swirl->knots)
(void) free((void *) swirl->knots);
}
/****************************************************************/
#ifndef STANDALONE
ENTRYPOINT void
refresh_swirl (ModeInfo * mi)
{
SWIRL_P swirl = &(swirls[MI_SCREEN(mi)]);
if (swirl->started) {
if (swirl->drawing)
swirl->resolution = swirl->resolution + 1;
swirl->drawing = False;
}
}
#endif
XSCREENSAVER_MODULE ("Swirl", swirl)