http://svn.poeml.de/viewvc/ppc/src-unpacked/xscreensaver/xscreensaver-4.12.tar.bz2...
[xscreensaver] / hacks / halftone.c
1 /* halftone, Copyright (c) 2002 by Peter Jaric <peter@jaric.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Description:
12  * Draws the gravitational force in each point on the screen seen
13  * through a halftone dot pattern. The force is calculated from a set
14  * of moving mass points. View it from a distance for best effect.
15  */
16
17 #include <math.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include "screenhack.h"
21
22 #define DEFAULT_DELAY          10000
23 #define DEFAULT_SPACING        14
24 #define DEFAULT_SIZE_FACTOR    1.5
25 #define DEFAULT_COUNT          10
26 #define DEFAULT_MIN_MASS       0.001
27 #define DEFAULT_MAX_MASS       0.02
28 #define DEFAULT_MIN_SPEED      0.001
29 #define DEFAULT_MAX_SPEED      0.02
30
31 char *progclass = "Halftone";
32
33 char *defaults [] = {
34   "*delay:              10000",
35   "*count:              10",
36   "*minMass:            0.001",
37   "*maxMass:            0.02",
38   "*minSpeed:           0.001",
39   "*maxSpeed:           0.02",
40   "*spacing:            14",
41   "*sizeFactor:         1.5",
42   "*colors:             200",
43   "*cycleSpeed:         10",
44   0
45 };
46
47 XrmOptionDescRec options [] = {
48   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
49   { "-count",           ".count",       XrmoptionSepArg, 0 },
50   { "-minmass",         ".minMass",     XrmoptionSepArg, 0 },
51   { "-maxmass",         ".maxMass",     XrmoptionSepArg, 0 },
52   { "-minspeed",        ".minSpeed",    XrmoptionSepArg, 0 },
53   { "-maxspeed",        ".maxSpeed",    XrmoptionSepArg, 0 },
54   { "-spacing",         ".spacing",     XrmoptionSepArg, 0 },
55   { "-sizefactor",      ".sizeFactor",  XrmoptionSepArg, 0 },
56   { "-colors",          ".colors",      XrmoptionSepArg, 0 },
57   { "-cycle-speed",     ".cycleSpeed",  XrmoptionSepArg, 0 },
58   { 0, 0, 0, 0 }
59 };
60
61 typedef struct
62 {
63   /* halftone dots */
64   double * dots;
65   int dots_width;
66   int dots_height;
67   int spacing; 
68   int max_dot_size;
69
70   /* Moving gravity points */
71   int gravity_point_count;
72
73   double* gravity_point_x;
74   double* gravity_point_y;
75   double* gravity_point_mass;
76   double* gravity_point_x_inc;
77   double* gravity_point_y_inc;
78
79   /* X stuff */
80   Display *display;
81   Window window;
82   GC gc;
83
84   int ncolors;
85   XColor *colors;
86   int color0, color1;
87   int color_tick, cycle_speed;
88
89   /* Off screen buffer */
90   Pixmap buffer;
91   GC buffer_gc;
92   int buffer_width;
93   int buffer_height;
94 } halftone_screen;
95
96
97 static void update_buffer(halftone_screen *halftone, XWindowAttributes * attrs)
98 {
99   if (halftone->buffer_width != attrs->width ||
100       halftone->buffer_height != attrs->height)
101   {
102     XGCValues gc_values;
103
104     if (halftone->buffer_width != -1 &&
105         halftone->buffer_height != -1)
106     {
107       XFreePixmap(halftone->display, halftone->buffer);
108       XFreeGC(halftone->display, halftone->buffer_gc);
109     }
110
111     halftone->buffer_width = attrs->width;
112     halftone->buffer_height = attrs->height;
113     halftone->buffer = XCreatePixmap(halftone->display, halftone->window, halftone->buffer_width, halftone->buffer_height, attrs->depth);
114
115     halftone->buffer_gc = XCreateGC(halftone->display, halftone->buffer, GCForeground|GCBackground, &gc_values);
116   }
117 }
118
119 static void update_dot_attributes(halftone_screen *halftone, XWindowAttributes * attrs)
120 {  
121   double dots_width = attrs->width / halftone->spacing + 1;
122   double dots_height = attrs->height / halftone->spacing + 1;
123
124   if (halftone->dots == NULL ||
125       (dots_width != halftone->dots_width ||
126        dots_height != halftone->dots_height))
127   {
128     if (halftone->dots != NULL)
129       free(halftone->dots);
130
131     halftone->dots_width = dots_width;
132     halftone->dots_height = dots_height;
133     halftone->dots = (double *) malloc(halftone->dots_width * halftone->dots_height * sizeof(double));
134   }
135 }
136
137 static halftone_screen * init_halftone(Display *display, Window window)
138 {
139   int x, y, i;
140   int count;
141   int spacing;
142   double factor;
143   double min_mass;
144   double max_mass;
145   double min_speed;
146   double max_speed;
147   XGCValues gc_values;
148   XWindowAttributes attrs;
149   halftone_screen *halftone;
150
151   halftone = (halftone_screen *) calloc (1, sizeof(halftone_screen));
152
153   halftone->display = display;
154   halftone->window = window;
155
156   halftone->gc = XCreateGC (halftone->display, halftone->window, GCForeground | GCBackground, &gc_values);
157
158   halftone->buffer_width = -1;
159   halftone->buffer_height = -1;
160   halftone->dots = NULL;
161
162   /* Read command line arguments and set all settings. */ 
163   count = get_integer_resource ("count", "Count");
164   halftone->gravity_point_count = count < 1 ? DEFAULT_COUNT : count; 
165
166   spacing = get_integer_resource ("spacing", "Integer");
167   halftone->spacing = spacing < 1 ? DEFAULT_SPACING : spacing; 
168
169   factor = get_float_resource ("sizeFactor", "Double");
170   halftone->max_dot_size = 
171     (factor < 0 ? DEFAULT_SIZE_FACTOR : factor) * halftone->spacing; 
172
173   min_mass = get_float_resource ("minMass", "Double");
174   min_mass = min_mass < 0 ? DEFAULT_MIN_MASS : min_mass;
175
176   max_mass = get_float_resource ("maxMass", "Double");
177   max_mass = max_mass < 0 ? DEFAULT_MAX_MASS : max_mass;
178   max_mass = max_mass < min_mass ? min_mass : max_mass;
179
180   min_speed = get_float_resource ("minSpeed", "Double");
181   min_speed = min_speed < 0 ? DEFAULT_MIN_SPEED : min_speed;
182
183   max_speed = get_float_resource ("maxSpeed", "Double");
184   max_speed = max_speed < 0 ? DEFAULT_MAX_SPEED : max_speed;
185   max_speed = max_speed < min_speed ? min_speed : max_speed;
186
187
188   /* Set up the moving gravity points. */
189   halftone->gravity_point_x = (double *) malloc(halftone->gravity_point_count * sizeof(double));
190   halftone->gravity_point_y = (double *) malloc(halftone->gravity_point_count * sizeof(double));
191   halftone->gravity_point_mass = (double *) malloc(halftone->gravity_point_count * sizeof(double));
192   halftone->gravity_point_x_inc = (double *) malloc(halftone->gravity_point_count * sizeof(double));
193   halftone->gravity_point_y_inc = (double *) malloc(halftone->gravity_point_count * sizeof(double));
194
195   for (i = 0; i < halftone->gravity_point_count; i++)
196   {
197     halftone->gravity_point_x[i] = frand(1);
198     halftone->gravity_point_y[i] = frand(1);
199     halftone->gravity_point_mass[i] = min_mass + (max_mass - min_mass) * frand(1);
200     halftone->gravity_point_x_inc[i] = min_speed + (max_speed - min_speed) * frand(1);
201     halftone->gravity_point_y_inc[i] = min_speed + (max_speed - min_speed) * frand(1);
202   }
203
204
205   /* Set up the dots. */
206   XGetWindowAttributes(halftone->display, halftone->window, &attrs);  
207
208   halftone->ncolors = get_integer_resource ("colors", "Colors");
209   if (halftone->ncolors < 4) halftone->ncolors = 4;
210   halftone->colors = (XColor *) calloc(halftone->ncolors, sizeof(XColor));
211   make_smooth_colormap (display, attrs.visual, attrs.colormap,
212                         halftone->colors, &halftone->ncolors,
213                         True, 0, False);
214   halftone->color0 = 0;
215   halftone->color1 = halftone->ncolors / 2;
216   halftone->cycle_speed = get_integer_resource ("cycleSpeed", "CycleSpeed");
217   halftone->color_tick = 0;
218
219   update_buffer(halftone, &attrs);
220   update_dot_attributes(halftone, &attrs);
221
222   for (x = 0; x < halftone->dots_width; x++)
223     for (y = 0; y < halftone->dots_height; y++)
224     {
225         halftone->dots[x + y * halftone->dots_width] = 0;
226     }
227
228   return halftone;
229 }
230
231
232
233 static void fill_circle(Display *display, Window window, GC gc, int x, int y, int size)
234 {
235   int start_x = x - (size / 2);
236   int start_y = y - (size / 2);
237   unsigned int width = size;
238   unsigned int height = size;
239   int angle1 = 0;
240   int angle2 = 360 * 64; /* A full circle */
241
242   XFillArc (display, window, gc,
243             start_x, start_y, width, height,
244             angle1, angle2);
245 }
246
247 static void repaint_halftone(halftone_screen *halftone)
248 {
249   int x, y;
250   /*
251   int x_offset = halftone->spacing / 2;
252   int y_offset = halftone->spacing / 2;
253   */
254   int x_offset = 0;
255   int y_offset = 0;
256
257   
258   /* Fill buffer with background color */
259   XSetForeground (halftone->display, halftone->buffer_gc,
260                   halftone->colors[halftone->color0].pixel);
261   XFillRectangle(halftone->display, halftone->buffer, halftone->buffer_gc, 0, 0, halftone->buffer_width, halftone->buffer_height);
262
263   /* Draw dots on buffer */
264   XSetForeground (halftone->display, halftone->buffer_gc,
265                   halftone->colors[halftone->color1].pixel);
266
267   if (halftone->color_tick++ >= halftone->cycle_speed)
268     {
269       halftone->color_tick = 0;
270       halftone->color0 = (halftone->color0 + 1) % halftone->ncolors;
271       halftone->color1 = (halftone->color1 + 1) % halftone->ncolors;
272     }
273
274   for (x = 0; x < halftone->dots_width; x++)
275     for (y = 0; y < halftone->dots_height; y++)
276       fill_circle(halftone->display, halftone->buffer, halftone->buffer_gc,
277                   x_offset + x * halftone->spacing, y_offset + y * halftone->spacing, 
278                   halftone->max_dot_size * halftone->dots[x + y * halftone->dots_width]);
279
280   /* Copy buffer to window */
281   XCopyArea(halftone->display, halftone->buffer, halftone->window, halftone->gc, 0, 0, halftone->buffer_width, halftone->buffer_height, 0, 0);
282 }
283
284 static double calculate_gravity(halftone_screen *halftone, int x, int y)
285 {
286   int i;
287   double gx = 0;
288   double gy = 0;
289
290   for (i = 0; i < halftone->gravity_point_count; i++)
291   {
292     double dx = ((double) x) - halftone->gravity_point_x[i] * halftone->dots_width; 
293     double dy = ((double) y) - halftone->gravity_point_y[i] * halftone->dots_height; 
294     double distance = sqrt(dx * dx + dy * dy);
295     
296     if (distance != 0)
297     {
298       double gravity = halftone->gravity_point_mass[i] / (distance * distance  / (halftone->dots_width * halftone->dots_height));
299       
300       gx += (dx / distance) * gravity;
301       gy += (dy / distance) * gravity;
302     }
303   }  
304   
305   return sqrt(gx * gx + gy * gy);
306 }
307
308 static void update_halftone(halftone_screen *halftone)
309 {
310   int x, y, i;
311   XWindowAttributes attrs;
312
313   XGetWindowAttributes(halftone->display, halftone->window, &attrs);
314
315   /* Make sure we have a valid buffer */
316   update_buffer(halftone, &attrs);
317   
318   /* Make sure all dot attributes (spacing, width, height, etc) are correct */
319   update_dot_attributes(halftone, &attrs);
320   
321   /* Move gravity points */
322   for (i = 0; i < halftone->gravity_point_count; i++)
323   {
324     halftone->gravity_point_x_inc[i] = 
325       (halftone->gravity_point_x[i] >= 1 || halftone->gravity_point_x[i] <= 0 ?
326        -halftone->gravity_point_x_inc[i] : 
327        halftone->gravity_point_x_inc[i]);
328     halftone->gravity_point_y_inc[i] = 
329       (halftone->gravity_point_y[i] >= 1 || halftone->gravity_point_y[i] <= 0 ?
330        -halftone->gravity_point_y_inc[i] : 
331        halftone->gravity_point_y_inc[i]);
332
333     halftone->gravity_point_x[i] += halftone->gravity_point_x_inc[i];
334     halftone->gravity_point_y[i] += halftone->gravity_point_y_inc[i];
335   }
336
337   /* Update gravity in each dot .*/
338   for (x = 0; x < halftone->dots_width; x++)
339     for (y = 0; y < halftone->dots_height; y++)
340     {
341       double gravity = calculate_gravity(halftone, x, y);
342
343       halftone->dots[x + y * halftone->dots_width] = (gravity > 1 ? 1 : (gravity < 0 ? 0 : gravity));
344     }
345 }
346
347
348 void screenhack (Display *display, Window window)
349 {
350   halftone_screen *halftone = init_halftone(display, window);
351   int delay = get_integer_resource ("delay", "Integer");
352   delay = (delay < 0 ? DEFAULT_DELAY : delay);
353
354   while (1)
355     {
356       repaint_halftone(halftone);
357       update_halftone(halftone);
358       screenhack_handle_events (display);
359
360       if (delay != 0) 
361         usleep (delay);
362     }
363 }