1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lightning --- fractal lightning bolds */
5 static const char sccsid[] = "@(#)lightning.c 5.00 2000/11/01 xlockmore";
9 * Copyright (c) 1996 by Keith Romberg <kromberg@saxe.com>
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appear in all copies and that
14 * both that copyright notice and this permission notice appear in
15 * supporting documentation.
17 * This file is provided AS IS with no warranties of any kind. The author
18 * shall have no liability with respect to the infringement of copyrights,
19 * trade secrets or any patents by this file or any part thereof. In no
20 * event will the author be liable for any lost revenue or profits or
21 * other special, indirect and consequential damages.
24 * 01-Nov-2000: Allocation checks
25 * 10-May-1997: Compatible with xscreensaver
26 * 14-Jul-1996: Cleaned up code.
27 * 27-Jun-1996: Written and submitted by Keith Romberg <kromberg@saxe.com>.
31 # define MODE_lightning
32 # define DEFAULTS "*delay: 10000 \n" \
35 # define BRIGHT_COLORS
36 # define reshape_lightning 0
37 # define lightning_handle_event 0
38 # include "xlockmore.h" /* in xscreensaver distribution */
39 #else /* STANDALONE */
40 # include "xlock.h" /* in xlockmore distribution */
41 #endif /* STANDALONE */
45 ENTRYPOINT ModeSpecOpt lightning_opts =
46 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
49 ModStruct lightning_description =
50 {"lightning", "init_lightning", "draw_lightning", "release_lightning",
51 "refresh_lightning", "init_lightning", (char *) NULL, &lightning_opts,
52 10000, 1, 1, 1, 64, 0.6, "",
53 "Shows Keith's fractal lightning bolts", 0, NULL};
58 #define BOLT_ITERATION 4
59 #define LONG_FORK_ITERATION 3
60 #define MEDIUM_FORK_ITERATION 2
61 #define SMALL_FORK_ITERATION 1
63 #define WIDTH_VARIATION 30
64 #define HEIGHT_VARIATION 15
66 #define DELAY_TIME_AMOUNT 15
67 #define MULTI_DELAY_TIME_BASE 5
69 #define MAX_WIGGLES 16
71 #define WIGGLE_AMOUNT 14
73 #define RANDOM_FORK_PROBILITY 4
75 #define FIRST_LEVEL_STRIKE 0
76 #define LEVEL_ONE_STRIKE 1
77 #define LEVEL_TWO_STRIKE 2
79 #define BOLT_VERTICIES ((1<<BOLT_ITERATION)-1)
80 /* BOLT_ITERATION = 4. 2^(BOLT_ITERATION) - 1 = 15 */
82 #define NUMBER_FORK_VERTICIES 9
84 #define FLASH_PROBILITY 20
85 #define MAX_FLASH_AMOUNT 2 /* half the total duration of the bolt */
88 XPoint ForkVerticies[NUMBER_FORK_VERTICIES];
94 XPoint middle[BOLT_VERTICIES];
102 int flash_begin, flash_stop;
108 Lightning bolts[BOLT_NUMBER];
109 int scr_width, scr_height;
118 static Storm *Helga = (Storm *) NULL;
120 /*------------------- function prototypes ----------------------------*/
122 static int distance(XPoint a, XPoint b);
124 static int setup_multi_strike(void);
125 static int flashing_strike(void);
126 static void flash_duration(int *start, int *end, int total_duration);
127 static void random_storm(Storm * st);
128 static void generate(XPoint A, XPoint B, int iter, XPoint * verts, int *vert_index);
129 static void create_fork(Fork * f, XPoint start, XPoint end, int level);
131 static void first_strike(Lightning bolt, ModeInfo * mi);
132 static void draw_bolt(Lightning * bolt, ModeInfo * mi);
133 static void draw_line(ModeInfo * mi, XPoint * p, int number, GC use, int x_offset);
134 static void level1_strike(Lightning bolt, ModeInfo * mi);
135 static void level2_strike(Lightning bolt, ModeInfo * mi);
137 static int storm_active(Storm * st);
138 static void update_bolt(Lightning * bolt, int time_now);
139 static void wiggle_bolt(Lightning * bolt);
140 static void wiggle_line(XPoint * p, int number, int wiggle_amount);
142 /*------------------------- functions ---------------------------------*/
145 setup_multi_strike(void)
147 int result, multi_prob;
149 multi_prob = NRAND(100);
153 else if ((multi_prob >= 51) && (multi_prob < 75))
155 else if ((multi_prob >= 76) && (multi_prob < 92))
158 result = BOLT_NUMBER; /* 4 */
163 /*-------------------------------------------------------------------------*/
166 flashing_strike(void)
168 int tmp = NRAND(FLASH_PROBILITY);
170 if (tmp <= FLASH_PROBILITY)
175 /*-------------------------------------------------------------------------*/
178 flash_duration(int *start, int *end, int total_duration)
182 mid = total_duration / MAX_FLASH_AMOUNT;
183 d = NRAND(total_duration / MAX_FLASH_AMOUNT) / 2;
188 /*-------------------------------------------------------------------------*/
191 random_storm(Storm * st)
196 for (i = 0; i < st->multi_strike; i++) {
197 st->bolts[i].end1.x = NRAND(st->scr_width);
198 st->bolts[i].end1.y = 0;
199 st->bolts[i].end2.x = NRAND(st->scr_width);
200 st->bolts[i].end2.y = st->scr_height;
201 st->bolts[i].wiggle_number = WIGGLE_BASE + NRAND(MAX_WIGGLES);
202 if ((st->bolts[i].flash = flashing_strike()))
203 flash_duration(&(st->bolts[i].flash_begin), &(st->bolts[i].flash_stop),
204 st->bolts[i].wiggle_number);
206 st->bolts[i].flash_begin = st->bolts[i].flash_stop = 0;
207 st->bolts[i].wiggle_amount = WIGGLE_AMOUNT;
209 st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT);
211 st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT) +
212 (MULTI_DELAY_TIME_BASE * i);
213 st->bolts[i].strike_level = FIRST_LEVEL_STRIKE;
215 generate(st->bolts[i].end1, st->bolts[i].end2, BOLT_ITERATION,
216 st->bolts[i].middle, &tmp);
217 st->bolts[i].fork_number = 0;
218 st->bolts[i].visible = 0;
219 for (j = 0; j < BOLT_VERTICIES; j++) {
220 if (st->bolts[i].fork_number >= 2)
222 if (NRAND(100) < RANDOM_FORK_PROBILITY) {
223 p.x = NRAND(st->scr_width);
224 p.y = st->scr_height;
225 st->bolts[i].forks_start[st->bolts[i].fork_number] = j;
226 create_fork(&(st->bolts[i].branch[st->bolts[i].fork_number]),
227 st->bolts[i].middle[j], p, j);
228 st->bolts[i].fork_number++;
235 generate(XPoint A, XPoint B, int iter, XPoint * verts, int *vert_index)
239 mid.x = (A.x + B.x) / 2 + NRAND(WIDTH_VARIATION) - WIDTH_VARIATION / 2;
240 mid.y = (A.y + B.y) / 2 + NRAND(HEIGHT_VARIATION) - HEIGHT_VARIATION / 2;
243 verts[*vert_index].x = mid.x;
244 verts[*vert_index].y = mid.y;
248 generate(A, mid, iter - 1, verts, vert_index);
249 generate(mid, B, iter - 1, verts, vert_index);
252 /*------------------------------------------------------------------------*/
255 create_fork(Fork * f, XPoint start, XPoint end, int level)
259 f->ForkVerticies[0].x = start.x;
260 f->ForkVerticies[0].y = start.y;
263 generate(start, end, LONG_FORK_ITERATION, f->ForkVerticies, &tmp);
265 } else if ((level > 6) && (level <= 11)) {
266 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
269 if (distance(start, end) > 100) {
270 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
273 generate(start, end, SMALL_FORK_ITERATION, f->ForkVerticies, &tmp);
278 f->ForkVerticies[f->num_used - 1].x = end.x;
279 f->ForkVerticies[f->num_used - 1].y = end.y;
282 /*------------------------------------------------------------------------*/
285 update_bolt(Lightning * bolt, int time_now)
288 if ((bolt->wiggle_amount == 0) && (bolt->wiggle_number > 2))
289 bolt->wiggle_number = 0;
290 if (((time_now % 3) == 0))
291 bolt->wiggle_amount++;
293 if (((time_now >= bolt->delay_time) && (time_now < bolt->flash_begin)) ||
294 (time_now > bolt->flash_stop))
299 if (time_now == bolt->delay_time)
300 bolt->strike_level = FIRST_LEVEL_STRIKE;
301 else if (time_now == (bolt->delay_time + 1))
302 bolt->strike_level = LEVEL_ONE_STRIKE;
303 else if ((time_now > (bolt->delay_time + 1)) &&
304 (time_now <= (bolt->delay_time + bolt->flash_begin - 2)))
305 bolt->strike_level = LEVEL_TWO_STRIKE;
306 else if (time_now == (bolt->delay_time + bolt->flash_begin - 1))
307 bolt->strike_level = LEVEL_ONE_STRIKE;
308 else if (time_now == (bolt->delay_time + bolt->flash_stop + 1))
309 bolt->strike_level = LEVEL_ONE_STRIKE;
311 bolt->strike_level = LEVEL_TWO_STRIKE;
314 /*------------------------------------------------------------------------*/
317 draw_bolt(Lightning * bolt, ModeInfo * mi)
320 if (bolt->strike_level == FIRST_LEVEL_STRIKE)
321 first_strike(*bolt, mi);
322 else if (bolt->strike_level == LEVEL_ONE_STRIKE)
323 level1_strike(*bolt, mi);
325 level2_strike(*bolt, mi);
329 /*------------------------------------------------------------------------*/
332 first_strike(Lightning bolt, ModeInfo * mi)
334 Display *display = MI_DISPLAY(mi);
335 Window window = MI_WINDOW(mi);
339 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
340 XDrawLine(display, window, gc,
341 bolt.end1.x, bolt.end1.y, bolt.middle[0].x, bolt.middle[0].y);
342 draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 0);
343 XDrawLine(display, window, gc,
344 bolt.middle[BOLT_VERTICIES - 1].x, bolt.middle[BOLT_VERTICIES - 1].y,
345 bolt.end2.x, bolt.end2.y);
347 for (i = 0; i < bolt.fork_number; i++)
348 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
352 /*------------------------------------------------------------------------*/
355 draw_line(ModeInfo * mi, XPoint * points, int number, GC to_use, int offset)
359 for (i = 0; i < number - 1; i++) {
360 if (points[i].y <= points[i + 1].y)
361 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x + offset,
362 points[i].y, points[i + 1].x + offset, points[i + 1].y);
364 if (points[i].x < points[i + 1].x)
365 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x +
366 offset, points[i].y + offset, points[i + 1].x + offset,
367 points[i + 1].y + offset);
369 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x -
370 offset, points[i].y + offset, points[i + 1].x - offset,
371 points[i + 1].y + offset);
376 /*------------------------------------------------------------------------*/
379 level1_strike(Lightning bolt, ModeInfo * mi)
381 Display *display = MI_DISPLAY(mi);
382 Window window = MI_WINDOW(mi);
383 Storm *st = &Helga[MI_SCREEN(mi)];
387 if (MI_NPIXELS(mi) > 2) /* color */
388 XSetForeground(display, gc, MI_PIXEL(mi, st->color));
390 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
391 XDrawLine(display, window, gc,
392 bolt.end1.x - 1, bolt.end1.y, bolt.middle[0].x - 1, bolt.middle[0].y);
393 draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -1);
394 XDrawLine(display, window, gc,
395 bolt.middle[BOLT_VERTICIES - 1].x - 1,
396 bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 1, bolt.end2.y);
397 XDrawLine(display, window, gc,
398 bolt.end1.x + 1, bolt.end1.y, bolt.middle[0].x + 1, bolt.middle[0].y);
399 draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 1);
400 XDrawLine(display, window, gc,
401 bolt.middle[BOLT_VERTICIES - 1].x + 1,
402 bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 1, bolt.end2.y);
404 for (i = 0; i < bolt.fork_number; i++) {
405 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
407 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
410 first_strike(bolt, mi);
413 /*------------------------------------------------------------------------*/
416 distance(XPoint a, XPoint b)
418 return ((int) sqrt((double) (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
421 /*------------------------------------------------------------------------*/
424 level2_strike(Lightning bolt, ModeInfo * mi)
426 Display *display = MI_DISPLAY(mi);
427 Window window = MI_WINDOW(mi);
428 Storm *st = &Helga[MI_SCREEN(mi)];
432 /* This was originally designed to be a little darker then the
433 level1 strike. This was changed to get it to work on
434 multiscreens and to add more color variety. I tried
435 stippling but it did not look good. */
436 if (MI_NPIXELS(mi) > 2)
437 XSetForeground(display, gc, MI_PIXEL(mi, st->color));
439 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
440 XDrawLine(display, window, gc,
441 bolt.end1.x - 2, bolt.end1.y, bolt.middle[0].x - 2, bolt.middle[0].y);
442 draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -2);
443 XDrawLine(display, window, gc,
444 bolt.middle[BOLT_VERTICIES - 1].x - 2,
445 bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 2, bolt.end2.y);
447 XDrawLine(display, window, gc,
448 bolt.end1.x + 2, bolt.end1.y, bolt.middle[0].x + 2, bolt.middle[0].y);
449 draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 2);
450 XDrawLine(display, window, gc,
451 bolt.middle[BOLT_VERTICIES - 1].x + 2,
452 bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 2, bolt.end2.y);
454 for (i = 0; i < bolt.fork_number; i++) {
455 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
457 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
460 level1_strike(bolt, mi);
463 /*------------------------------------------------------------------------*/
466 storm_active(Storm * st)
468 int i, atleast_1 = 0;
470 for (i = 0; i < st->multi_strike; i++)
471 if (st->bolts[i].wiggle_number > 0)
477 /*------------------------------------------------------------------------*/
480 wiggle_bolt(Lightning * bolt)
484 wiggle_line(bolt->middle, BOLT_VERTICIES, bolt->wiggle_amount);
485 bolt->end2.x += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2;
486 bolt->end2.y += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2;
488 for (i = 0; i < bolt->fork_number; i++) {
489 wiggle_line(bolt->branch[i].ForkVerticies, bolt->branch[i].num_used,
490 bolt->wiggle_amount);
491 bolt->branch[i].ForkVerticies[0].x = bolt->middle[bolt->forks_start[i]].x;
492 bolt->branch[i].ForkVerticies[0].y = bolt->middle[bolt->forks_start[i]].y;
495 if (bolt->wiggle_amount > 1)
496 bolt->wiggle_amount -= 1;
498 bolt->wiggle_amount = 0;
501 /*------------------------------------------------------------------------*/
504 wiggle_line(XPoint * p, int number, int amount)
508 for (i = 0; i < number; i++) {
509 p[i].x += NRAND(amount) - amount / 2;
510 p[i].y += NRAND(amount) - amount / 2;
514 /*------------------------------------------------------------------------*/
517 init_lightning (ModeInfo * mi)
522 if ((Helga = (Storm *) calloc(MI_NUM_SCREENS(mi),
523 sizeof (Storm))) == NULL)
526 st = &Helga[MI_SCREEN(mi)];
528 st->scr_width = MI_WIDTH(mi);
529 st->scr_height = MI_HEIGHT(mi);
531 st->multi_strike = setup_multi_strike();
536 /*------------------------------------------------------------------------*/
539 draw_lightning (ModeInfo * mi)
546 st = &Helga[MI_SCREEN(mi)];
547 MI_IS_DRAWN(mi) = True;
550 MI_IS_DRAWN(mi) = False;
552 MI_IS_DRAWN(mi) = True;
554 st->color = NRAND(MI_NPIXELS(mi));
556 if (storm_active(st))
562 for (i = 0; i < st->multi_strike; i++) {
563 if (st->bolts[i].visible)
564 draw_bolt(&(st->bolts[i]), mi);
565 update_bolt(&(st->bolts[i]), st->draw_time);
572 if (++st->busyLoop > 6) {
578 MI_IS_DRAWN(mi) = False;
580 MI_IS_DRAWN(mi) = True;
582 if (storm_active(st))
588 if (++st->busyLoop > 100) {
597 release_lightning(ModeInfo * mi)
600 (void) free((void *) Helga);
601 Helga = (Storm *) NULL;
606 refresh_lightning(ModeInfo * mi)
608 /* Do nothing, it will refresh by itself */
611 XSCREENSAVER_MODULE ("Lightning", lightning)
614 #endif /* MODE_lightning */