xenocara/app/xlockmore/modes/ifs.c
2006-11-26 11:07:42 +00:00

566 lines
14 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* ifs --- modified iterated functions system */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)ifs.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* Copyright (c) 1997 by Massimino Pascal <Pascal.Massimon@ens.fr>
*
* 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.
*
* If this mode is weird and you have an old MetroX server, it is buggy.
* There is a free SuSE-enhanced MetroX X server that is fine.
*
* When shown ifs, Diana Rose (4 years old) said, "It looks like dancing."
*
* Revision History:
* 22-Dec-2004: TDA: Replace Gauss_Rand with a real Gaussian. Fix
* glitches where x=0 or y=0.
* 01-Nov-2000: Allocation checks
* 10-May-1997: jwz@jwz.org: turned into a standalone program.
* Made it render into an offscreen bitmap and then copy
* that onto the screen, to reduce flicker.
*/
#ifdef STANDALONE
#define MODE_ifs
#define PROGCLASS "IFS"
#define HACK_INIT init_ifs
#define HACK_DRAW draw_ifs
#define ifs_opts xlockmore_opts
#define DEFAULTS "*delay: 1000 \n" \
"*ncolors: 100 \n"
#define SMOOTH_COLORS
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#ifdef MODE_ifs
ModeSpecOpt ifs_opts =
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
#ifdef USE_MODULES
ModStruct ifs_description =
{"ifs", "init_ifs", "draw_ifs", "release_ifs",
"init_ifs", "init_ifs", (char *) NULL, &ifs_opts,
1000, 1, 1, 1, 64, 1.0, "",
"Shows a modified iterated function system", 0, NULL};
#endif
/*****************************************************/
typedef float DBL;
typedef int F_PT;
/* typedef float F_PT; */
/*****************************************************/
#define FIX 12
#define UNIT ( 1<<FIX )
#define MAX_SIMI 6
/* settings for a PC 120Mhz... */
#define MAX_DEPTH_2 10
#define MAX_DEPTH_3 6
#define MAX_DEPTH_4 4
#define MAX_DEPTH_5 3
#define DBL_To_F_PT(x) (F_PT)( (DBL)(UNIT)*(x) )
typedef struct Similitude_Struct SIMI;
typedef struct Fractal_Struct FRACTAL;
struct Similitude_Struct {
DBL c_x, c_y;
DBL r, r2, A, A2;
F_PT Ct, St, Ct2, St2;
F_PT Cx, Cy;
F_PT R, R2;
};
struct Fractal_Struct {
int Nb_Simi;
SIMI Components[5 * MAX_SIMI];
int Depth, Col;
int Count, Speed;
int Width, Height, Lx, Ly;
DBL r_mean, dr_mean, dr2_mean;
int Cur_Pt, Max_Pt;
XPoint *Buffer1, *Buffer2;
Pixmap dbuf;
GC dbuf_gc;
};
static FRACTAL *Root = (FRACTAL *) NULL, *Cur_F;
static XPoint *Buf;
static int Cur_Pt;
/*****************************************************/
#if 0
static DBL
Gauss_Rand(DBL c, DBL A, DBL S)
{
DBL y;
y = (DBL) LRAND() / MAXRAND;
y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
if (NRAND(2))
return (c + y);
return (c - y);
}
static DBL
Half_Gauss_Rand(DBL c, DBL A, DBL S)
{
DBL y;
y = (DBL) LRAND() / MAXRAND;
y = A * (1.0 - exp(-y * y * S)) / (1.0 - exp(-S));
return (c + y);
}
#else
/* I don't know that it makes a difference, but these actually *are*
Gaussians. [TDA] */
/* Generate Gaussian random number: mean c, "amplitude" A (actually
A is 3*standard deviation). 'S' is unused. */
/* Note this generates a pair of gaussian variables, so it saves one
to give out next time it's called */
static double
Gauss_Rand(DBL c, DBL A, DBL S)
{
static double d;
static Bool ready = 0;
if(ready) {
ready = 0;
return c + A/3 * d;
} else {
double x, y, w;
do {
x = 2.0 * (double)LRAND() / MAXRAND - 1.0;
y = 2.0 * (double)LRAND() / MAXRAND - 1.0;
w = x*x + y*y;
} while(w >= 1.0);
w = sqrt((-2 * log(w))/w);
ready = 1;
d = x * w;
return c + A/3 * y * w;
}
}
static double
Half_Gauss_Rand(DBL c, DBL A, DBL S)
{
static double d;
static Bool ready = 0;
if(ready) {
ready = 0;
return c + A/3 * d;
} else {
double x, y, w;
do {
x = (double)LRAND() / MAXRAND;
y = (double)LRAND() / MAXRAND;
w = x*x + y*y;
} while(w >= 1.0);
w = sqrt((-2 * log(w))/w);
ready = 1;
d = x * w;
return c + A/3 * y * w;
}
}
#endif
static void
Random_Simis(FRACTAL * F, SIMI * Cur, int i)
{
while (i--) {
Cur->c_x = Gauss_Rand(0.0, .8, 4.0);
Cur->c_y = Gauss_Rand(0.0, .8, 4.0);
Cur->r = Gauss_Rand(F->r_mean, F->dr_mean, 3.0);
Cur->r2 = Half_Gauss_Rand(0.0, F->dr2_mean, 2.0);
Cur->A = Gauss_Rand(0.0, 360.0, 4.0) * (M_PI / 180.0);
Cur->A2 = Gauss_Rand(0.0, 360.0, 4.0) * (M_PI / 180.0);
Cur++;
}
}
static void
free_ifs_buffers(FRACTAL *Fractal)
{
if (Fractal->Buffer1 != NULL) {
free(Fractal->Buffer1);
Fractal->Buffer1 = (XPoint *) NULL;
}
if (Fractal->Buffer2 != NULL) {
free(Fractal->Buffer2);
Fractal->Buffer2 = (XPoint *) NULL;
}
}
static void
free_ifs(Display *display, FRACTAL *Fractal)
{
free_ifs_buffers(Fractal);
if (Fractal->dbuf != None) {
XFreePixmap(display, Fractal->dbuf);
Fractal->dbuf = None;
}
if (Fractal->dbuf_gc != None) {
XFreeGC(display, Fractal->dbuf_gc);
Fractal->dbuf_gc = None;
}
}
/***************************************************************/
void
init_ifs(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
int i;
FRACTAL *Fractal;
if (Root == NULL) {
Root = (FRACTAL *) calloc(
MI_NUM_SCREENS(mi), sizeof (FRACTAL));
if (Root == NULL)
return;
}
Fractal = &Root[MI_SCREEN(mi)];
free_ifs_buffers(Fractal);
i = (NRAND(4)) + 2; /* Number of centers */
switch (i) {
case 3:
Fractal->Depth = MAX_DEPTH_3;
Fractal->r_mean = .6;
Fractal->dr_mean = .4;
Fractal->dr2_mean = .3;
break;
case 4:
Fractal->Depth = MAX_DEPTH_4;
Fractal->r_mean = .5;
Fractal->dr_mean = .4;
Fractal->dr2_mean = .3;
break;
case 5:
Fractal->Depth = MAX_DEPTH_5;
Fractal->r_mean = .5;
Fractal->dr_mean = .4;
Fractal->dr2_mean = .3;
break;
default:
case 2:
Fractal->Depth = MAX_DEPTH_2;
Fractal->r_mean = .7;
Fractal->dr_mean = .3;
Fractal->dr2_mean = .4;
break;
}
/* (void) fprintf( stderr, "N=%d\n", i ); */
Fractal->Nb_Simi = i;
Fractal->Max_Pt = Fractal->Nb_Simi - 1;
for (i = 0; i <= Fractal->Depth + 2; ++i)
Fractal->Max_Pt *= Fractal->Nb_Simi;
if ((Fractal->Buffer1 = (XPoint *) calloc(Fractal->Max_Pt,
sizeof (XPoint))) == NULL) {
free_ifs(display, Fractal);
return;
}
if ((Fractal->Buffer2 = (XPoint *) calloc(Fractal->Max_Pt,
sizeof (XPoint))) == NULL) {
free_ifs(display, Fractal);
return;
}
Fractal->Speed = 6;
Fractal->Width = MI_WIDTH(mi);
Fractal->Height = MI_HEIGHT(mi);
Fractal->Cur_Pt = 0;
Fractal->Count = 0;
Fractal->Lx = (Fractal->Width - 1) / 2;
Fractal->Ly = (Fractal->Height - 1) / 2;
Fractal->Col = NRAND(MI_NPIXELS(mi) - 1) + 1;
Random_Simis(Fractal, Fractal->Components, 5 * MAX_SIMI);
#ifndef NO_DBUF
if (Fractal->dbuf != None)
XFreePixmap(display, Fractal->dbuf);
Fractal->dbuf = XCreatePixmap(display, window,
Fractal->Width, Fractal->Height, 1);
/* Allocation checked */
if (Fractal->dbuf != None) {
XGCValues gcv;
gcv.foreground = 0;
gcv.background = 0;
gcv.graphics_exposures = False;
gcv.function = GXcopy;
if (Fractal->dbuf_gc != None)
XFreeGC(display, Fractal->dbuf_gc);
if ((Fractal->dbuf_gc = XCreateGC(display, Fractal->dbuf,
GCForeground | GCBackground | GCGraphicsExposures | GCFunction,
&gcv)) == None) {
XFreePixmap(display, Fractal->dbuf);
Fractal->dbuf = None;
} else {
XFillRectangle(display, Fractal->dbuf,
Fractal->dbuf_gc, 0, 0, Fractal->Width, Fractal->Height);
XSetBackground(display, gc, MI_BLACK_PIXEL(mi));
XSetFunction(display, gc, GXcopy);
}
}
#endif
MI_CLEARWINDOW(mi);
/* don't want any exposure events from XCopyPlane */
XSetGraphicsExposures(display, gc, False);
}
/***************************************************************/
/* Should be taken care of already... but just in case */
#if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
#undef inline
#define inline /* */
#endif
static inline void
Transform(SIMI * Simi, F_PT xo, F_PT yo, F_PT * x, F_PT * y)
{
F_PT xx, yy;
xo = xo - Simi->Cx;
xo = (xo * Simi->R) / UNIT;
yo = yo - Simi->Cy;
yo = (yo * Simi->R) / UNIT;
xx = xo - Simi->Cx;
xx = (xx * Simi->R2) / UNIT;
yy = -yo - Simi->Cy;
yy = (yy * Simi->R2) / UNIT;
*x = ((xo * Simi->Ct - yo * Simi->St + xx * Simi->Ct2 - yy * Simi->St2) / UNIT) + Simi->Cx;
*y = ((xo * Simi->St + yo * Simi->Ct + xx * Simi->St2 + yy * Simi->Ct2) / UNIT) + Simi->Cy;
}
/***************************************************************/
static void
Trace(FRACTAL * F, F_PT xo, F_PT yo)
{
F_PT x, y, i;
SIMI *Cur;
Cur = Cur_F->Components;
for (i = Cur_F->Nb_Simi; i; --i, Cur++) {
Transform(Cur, xo, yo, &x, &y);
Buf->x = (short) (F->Lx + ((double)x * F->Lx / (UNIT * 2)));
Buf->y = (short) (F->Ly - ((double)y * F->Ly / (UNIT * 2)));
Buf++;
Cur_Pt++;
if (F->Depth && ((x - xo) >> 4) && ((y - yo) >> 4)) {
F->Depth--;
Trace(F, x, y);
F->Depth++;
}
}
}
static void
Draw_Fractal(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
FRACTAL *F = &Root[MI_SCREEN(mi)];
int i, j;
F_PT x, y, xo, yo;
SIMI *Cur, *Simi;
for (Cur = F->Components, i = F->Nb_Simi; i; --i, Cur++) {
Cur->Cx = DBL_To_F_PT(Cur->c_x);
Cur->Cy = DBL_To_F_PT(Cur->c_y);
Cur->Ct = DBL_To_F_PT(cos(Cur->A));
Cur->St = DBL_To_F_PT(sin(Cur->A));
Cur->Ct2 = DBL_To_F_PT(cos(Cur->A2));
Cur->St2 = DBL_To_F_PT(sin(Cur->A2));
Cur->R = DBL_To_F_PT(Cur->r);
Cur->R2 = DBL_To_F_PT(Cur->r2);
}
Cur_Pt = 0;
Cur_F = F;
Buf = F->Buffer2;
for (Cur = F->Components, i = F->Nb_Simi; i; --i, Cur++) {
xo = Cur->Cx;
yo = Cur->Cy;
for (Simi = F->Components, j = F->Nb_Simi; j; --j, Simi++) {
if (Simi == Cur)
continue;
Transform(Simi, xo, yo, &x, &y);
Trace(F, x, y);
}
}
/* Erase previous */
if (F->Cur_Pt) {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
if (F->dbuf != None) {
XSetForeground(display, F->dbuf_gc, 0);
/* XDrawPoints(display, F->dbuf, F->dbuf_gc, F->Buffer1, F->Cur_Pt,
CoordModeOrigin); */
XFillRectangle(display, F->dbuf, F->dbuf_gc, 0, 0,
F->Width, F->Height);
} else
XDrawPoints(display, window, gc, F->Buffer1, F->Cur_Pt, CoordModeOrigin);
}
if (MI_NPIXELS(mi) < 2)
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
else
XSetForeground(display, gc, MI_PIXEL(mi, F->Col % MI_NPIXELS(mi)));
if (Cur_Pt) {
if (F->dbuf != None) {
XSetForeground(display, F->dbuf_gc, 1);
XDrawPoints(display, F->dbuf, F->dbuf_gc, F->Buffer2, Cur_Pt,
CoordModeOrigin);
} else
XDrawPoints(display, window, gc, F->Buffer2, Cur_Pt, CoordModeOrigin);
}
if (F->dbuf != None)
XCopyPlane(display, F->dbuf, window, gc, 0, 0, F->Width, F->Height, 0, 0, 1);
F->Cur_Pt = Cur_Pt;
Buf = F->Buffer1;
F->Buffer1 = F->Buffer2;
F->Buffer2 = Buf;
}
void
draw_ifs(ModeInfo * mi)
{
int i;
DBL u, uu, v, vv, u0, u1, u2, u3;
SIMI *S, *S1, *S2, *S3, *S4;
FRACTAL *F;
if (Root == NULL)
return;
F = &Root[MI_SCREEN(mi)];
if (F->Buffer1 == NULL)
return;
u = (DBL) (F->Count) * (DBL) (F->Speed) / 1000.0;
uu = u * u;
v = 1.0 - u;
vv = v * v;
u0 = vv * v;
u1 = 3.0 * vv * u;
u2 = 3.0 * v * uu;
u3 = u * uu;
S = F->Components;
S1 = S + F->Nb_Simi;
S2 = S1 + F->Nb_Simi;
S3 = S2 + F->Nb_Simi;
S4 = S3 + F->Nb_Simi;
for (i = F->Nb_Simi; i; --i, S++, S1++, S2++, S3++, S4++) {
S->c_x = u0 * S1->c_x + u1 * S2->c_x + u2 * S3->c_x + u3 * S4->c_x;
S->c_y = u0 * S1->c_y + u1 * S2->c_y + u2 * S3->c_y + u3 * S4->c_y;
S->r = u0 * S1->r + u1 * S2->r + u2 * S3->r + u3 * S4->r;
S->r2 = u0 * S1->r2 + u1 * S2->r2 + u2 * S3->r2 + u3 * S4->r2;
S->A = u0 * S1->A + u1 * S2->A + u2 * S3->A + u3 * S4->A;
S->A2 = u0 * S1->A2 + u1 * S2->A2 + u2 * S3->A2 + u3 * S4->A2;
}
MI_IS_DRAWN(mi) = True;
Draw_Fractal(mi);
if (F->Count >= 1000 / F->Speed) {
S = F->Components;
S1 = S + F->Nb_Simi;
S2 = S1 + F->Nb_Simi;
S3 = S2 + F->Nb_Simi;
S4 = S3 + F->Nb_Simi;
for (i = F->Nb_Simi; i; --i, S++, S1++, S2++, S3++, S4++) {
S2->c_x = 2.0 * S4->c_x - S3->c_x;
S2->c_y = 2.0 * S4->c_y - S3->c_y;
S2->r = 2.0 * S4->r - S3->r;
S2->r2 = 2.0 * S4->r2 - S3->r2;
S2->A = 2.0 * S4->A - S3->A;
S2->A2 = 2.0 * S4->A2 - S3->A2;
*S1 = *S4;
}
Random_Simis(F, F->Components + 3 * F->Nb_Simi, F->Nb_Simi);
Random_Simis(F, F->Components + 4 * F->Nb_Simi, F->Nb_Simi);
F->Count = 0;
} else
F->Count++;
F->Col++;
}
/***************************************************************/
void
release_ifs(ModeInfo * mi)
{
if (Root != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_ifs(MI_DISPLAY(mi), &Root[screen]);
free(Root);
Root = (FRACTAL *) NULL;
}
}
#endif /* MODE_ifs */