60d4d9cc503093e329c0e884fd5ea1659fa57069
[xscreensaver] / hacks / ifs.c
1 /*Copyright © Chris Le Sueur (thefishface@gmail.com) February 2005
2
3 Permission is hereby granted, free of charge, to any person obtaining
4 a copy of this software and associated documentation files (the
5 "Software"), to deal in the Software without restriction, including
6 without limitation the rights to use, copy, modify, merge, publish,
7 distribute, sublicense, and/or sell copies of the Software, and to
8 permit persons to whom the Software is furnished to do so, subject to
9 the following conditions:
10
11 The above copyright notice and this permission notice shall be included
12 in all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 OTHER DEALINGS IN THE SOFTWARE.
21
22 Ultimate thanks go to Massimino Pascal, who created the original
23 xscreensaver hack, and inspired me with it's swirly goodness. This
24 version adds things like variable quality, number of functions and also
25 a groovier colouring mode.
26
27 */
28
29 #include <assert.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <math.h>
34
35 #include "screenhack.h"
36
37 static float myrandom(float up)
38 {
39   return(((float)random()/RAND_MAX)*up);
40 }
41
42 static int delay;
43 static int lensnum;
44 static int length;
45 static int mode;
46 static Bool notranslate, noscale, norotate;
47 float ht,wt;
48 float hs,ws;
49 int width,height;
50 float r;
51 long wcol;
52 int coloffset;
53
54 float nx,ny;
55
56 int count;
57
58 /*X stuff*/
59 GC gc;
60 Window w;
61 Display *dpy;
62 Pixmap backbuffer;
63 XColor *colours;
64 int ncolours;
65 int screen_num;
66
67 int blackColor, whiteColor;
68
69 char *progclass = "IFS";
70
71 char *defaults [] = {
72   ".lensnum:    3",
73   ".length:     9",
74   ".mode:     0",
75   ".colors:     200",
76   "*delay:      10000",
77   "*notranslate:  False",
78   "*noscale:    False",
79   "*norotate:   False",
80   0
81 };
82
83 XrmOptionDescRec options [] = {
84   { "-detail",    ".length",    XrmoptionSepArg, 0 },
85   { "-delay",   ".delay",   XrmoptionSepArg, 0 },
86   { "-mode",    ".mode",    XrmoptionSepArg, 0 },
87   { "-colors",    ".colors",    XrmoptionSepArg, 0 },
88   { "-functions", ".lensnum",   XrmoptionSepArg, 0 },
89   { "-notranslate", ".notranslate", XrmoptionNoArg,  "True" },
90   { "-noscale",   ".noscale",   XrmoptionNoArg,  "True" },
91   { "-norotate",  ".norotate",  XrmoptionNoArg,  "True" },
92   { 0, 0, 0, 0 }
93 };
94
95 /*Takes the average of two colours, with some nifty bit-shifting*/
96 static long blend(long c1, long c2)
97 {
98   long R1=(c1 & 0xFF0000) >> 16;
99   long R2=(c2 & 0xFF0000) >> 16;
100   long G1=(c1 & 0x00FF00) >> 8;
101   long G2=(c2 & 0x00FF00) >> 8;
102   long B1=(c1 & 0x0000FF);
103   long B2=(c2 & 0x0000FF);
104   
105   return (((R1+R2)/2 << 16) | ((G1+G2)/2 << 8) | ((B1+B2)/2));
106 }
107
108 /*Draw a point on the backbuffer*/
109 static void sp(float x, float y, long c)
110 {
111   x+=16;    x*=wt;
112   y=16.5-y; y*=ht;
113   if(x<0 || x>=width || y<0 || y>=height) return;
114   XSetForeground(dpy, gc, c);
115   XDrawPoint(dpy, backbuffer, gc, (int)x, (int)y);
116 }
117
118 /*Copy backbuffer to front buffer and clear backbuffer*/
119 static void draw(void)
120 {
121   XCopyArea(  dpy,
122               backbuffer, w,
123               gc,
124               0, 0,
125               width, height,
126               0, 0);
127   
128   XSetForeground(dpy, gc, blackColor);
129   XFillRectangle( dpy,
130                   backbuffer,
131                   gc,
132                   0, 0,
133                   width, height);
134 }
135
136 typedef struct {
137   float r,s,tx,ty;
138   /*Rotation, Scale, Translation X & Y*/
139   float ra,raa,sa,txa,tya;
140   
141   int co;
142 } Lens; 
143
144
145 static void CreateLens(float nr, 
146         float ns, 
147         float nx, 
148         float ny, 
149         int nco,
150         Lens *newlens)
151 {
152   newlens->ra=newlens->raa=newlens->sa=newlens->txa=newlens->tya=0;
153   if(!norotate) newlens->r=nr;
154   else newlens->r=0;
155     
156   if(!noscale) newlens->s=ns;
157   else newlens->s=0.5;
158     
159   if(!notranslate) {
160     newlens->tx=nx;
161     newlens->ty=ny;
162   } else {
163     newlens->tx=nx;
164     newlens->tx=ny;
165   }
166     
167   newlens->co=nco;
168 }
169   
170 static float stepx(float x, float y, Lens *l)
171 {
172   return l->s*cos(l->r)*x+l->s*sin(l->r)*y+l->tx;
173 }
174
175 static float stepy(float x, float y, Lens *l)
176 {
177   return l->s*sin(l->r)*-x+l->s*cos(l->r)*y+l->ty;
178 }
179
180 static void mutate(Lens *l)
181 {
182   if(!norotate) {
183     l->raa+=myrandom(0.002)-0.001;
184     l->ra+=l->raa;
185     l->r +=l->ra;
186     if(l->ra>0.07  || l->ra<-0.07)  l->ra/=1.4;
187     if(l->raa>0.005 || l->raa<-0.005) l->raa/=1.2;
188   }
189   if(!noscale) {
190     l->sa+=myrandom(0.01)-0.005;
191     l->s +=l->sa;
192     if(l->s>0.4) l->sa -=0.004;
193     if(l->s<-0.4) l->sa +=0.004;
194     if(l->sa>0.07  || l->sa<-0.07)  l->sa/=1.4;
195   }
196   if(!notranslate) {
197     l->txa+=myrandom(0.004)-0.002;
198     l->tya+=myrandom(0.004)-0.002;
199     l->tx+=l->txa;
200     l->ty+=l->tya;
201     if(l->tx>6)  l->txa-=0.004;
202     if(l->ty>6)  l->tya-=0.004;
203     if(l->tx<-6)  l->txa+=0.004;
204     if(l->ty<-6)  l->tya+=0.004;
205     if(l->txa>0.05 || l->txa<-0.05) l->txa/=1.7;
206     if(l->tya>0.05 || l->tya<-0.05) l->tya/=1.7;
207   }
208   
209   /*Groovy, colour-shifting functions!*/
210   l->co++;
211   l->co %= ncolours;
212 }
213
214 Lens **lenses;
215
216 /* Calls itself <lensnum> times - with results from each lens/function.  *
217  * After <length> calls to itself, it stops iterating and draws a point. */
218 static void iterate(float x, float y, long curcol, int length)
219 {
220   int i;
221   if(length == 0) {
222     sp(x,y,curcol);
223   } else {
224     /*iterate(lenses[0].stepx(x,y),lenses[0].stepy(x,y),length-1);
225       iterate(lenses[1].stepx(x,y),lenses[1].stepy(x,y),length-1);
226       iterate(lenses[2].stepx(x,y),lenses[2].stepy(x,y),length-1);*/
227     for(i=0;i<lensnum;i++) {
228       switch(mode) {
229       case 0 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), blend( curcol,colours[(int)lenses[i]->co].pixel ), length-1); break;
230       case 1 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), colours[(int)lenses[i]->co].pixel, length-1); break;
231       case 2 : iterate(stepx( x, y, lenses[i]), stepy( x, y, lenses[i]), curcol, length-1); break;
232       default: exit(0);
233       }
234     }
235   }
236   count++;
237 }
238
239 /* Come on and iterate, iterate, iterate and sing... *
240  * Yeah, this function just calls iterate, mutate,   *
241  * and then draws everything.                        */
242 static void step(void)
243 {
244   int i;
245   if(mode == 2) {
246     wcol++;
247     wcol %= ncolours;
248     iterate(0,0,colours[wcol].pixel,length);
249   } else {
250     iterate(0,0,0xFFFFFF,length);
251   }
252   
253   
254   count=0;
255   
256   for(i=0;i<lensnum;i++) {
257     mutate(lenses[i]);
258   }
259   draw();
260 }
261
262 static void init_ifs(void)
263 {
264   Window rw;
265   int i;
266   XWindowAttributes xgwa;
267   
268   delay = get_integer_resource("delay", "Delay");
269   length = get_integer_resource("length", "Detail");
270   mode = get_integer_resource("mode", "Mode");
271
272   norotate    = get_boolean_resource("norotate", "NoRotate");
273   noscale     = get_boolean_resource("noscale", "NoScale");
274   notranslate = get_boolean_resource("notranslate", "NoTranslate");
275
276   lensnum = get_integer_resource("lensnum", "Functions");
277   
278   lenses = malloc(sizeof(Lens)*lensnum);
279   
280   for(i=0;i<lensnum;i++) {
281     lenses[i]=malloc(sizeof(Lens));
282   }
283   
284   /*Thanks go to Dad for teaching me how to allocate memory for struct**s . */
285   
286   XGetWindowAttributes (dpy, w, &xgwa);
287   width=xgwa.width;
288   height=xgwa.height;
289   
290   /*Initialise all this X shizzle*/
291   blackColor = BlackPixel(dpy, DefaultScreen(dpy));
292   whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
293   rw = RootWindow(dpy, screen_num);
294   screen_num = DefaultScreen(dpy);
295   gc = XCreateGC(dpy, rw, 0, NULL);
296   
297   /* Do me some colourmap magic. If we're using blend mode, this is just   *
298    * for the nice colours - we're still using true/hicolour. Screw me if   *
299    * I'm going to work out how to blend with colourmaps - I'm too young to *
300    * die!! On a sidenote, this is mostly stolen from halftone because I    *
301    * don't really know what the hell I'm doing, here.                      */
302   ncolours = get_integer_resource("colors", "Colors");
303   if(ncolours < lensnum) ncolours=lensnum; /*apparently you're allowed to do this kind of thing...*/
304   colours = (XColor *)calloc(ncolours, sizeof(XColor));
305   make_smooth_colormap (  dpy,
306                           xgwa.visual,
307                           xgwa.colormap,
308                           colours,
309                           &ncolours,
310                           True, 0, False);
311   /*No, I didn't have a clue what that really did... hopefully I have some colours in an array, now.*/
312   wcol = (int)myrandom(ncolours);
313     
314   /*Double buffering - I can't be bothered working out the XDBE thingy*/
315   backbuffer = XCreatePixmap(dpy, w, width, height, XDefaultDepth(dpy, screen_num));
316   
317   /*Scaling factor*/
318   wt=width/32;
319   ht=height/24;
320   
321   ws=400;
322   hs=400;
323
324   /*Colourmapped colours for the general prettiness*/
325   for(i=0;i<lensnum;i++) {
326     CreateLens( myrandom(1)-0.5,
327                 myrandom(1),
328                 myrandom(4)-2,
329                 myrandom(4)+2,
330                 myrandom(ncolours),
331                 lenses[i]);
332   }
333 }
334
335 void
336 screenhack (Display *display, Window window)
337 {
338   dpy = display;
339   w   = window;
340   
341   init_ifs();
342   
343   while (1) {
344     step();
345     screenhack_handle_events(dpy);
346     if (delay) usleep(delay);
347   }
348 }