ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-2.07.tar.gz
[xscreensaver] / hacks / lightning.c
diff --git a/hacks/lightning.c b/hacks/lightning.c
new file mode 100644 (file)
index 0000000..baa255d
--- /dev/null
@@ -0,0 +1,580 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ * lightning --- fractal lightning bolts.
+ */
+#if !defined( lint ) && !defined( SABER )
+static const char sccsid[] = "@(#)lightning.c  4.00 97/01/01 xlockmore";
+#endif
+
+/* Copyright (c) 1996 by Keith Romberg <kromberg@saxe.com>.
+ *
+ * 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.
+ *
+ * Revision History:
+ * 10-May-97: jwz@netscape.com: turned into a standalone program.
+ * 14-Jul-96: Cleaned up code.
+ * 27-Jun-96: Written and submitted by Keith Romberg <kromberg@saxe.com>.
+ */
+
+#ifdef STANDALONE
+# define PROGCLASS                                     "Lightning"
+# define HACK_INIT                                     init_lightning
+# define HACK_DRAW                                     draw_lightning
+# define lightning_opts                                xlockmore_opts
+# define DEFAULTS      "*delay:                10000 \n"                       \
+                                       "*ncolors:              200   \n"
+# define BRIGHT_COLORS
+# include "xlockmore.h"                                /* from the xscreensaver distribution */
+#else  /* !STANDALONE */
+# include "xlock.h"                                    /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+ModeSpecOpt lightning_opts = {
+  0, NULL, 0, NULL, NULL };
+
+/*----------------------------   defines   -------------------------------*/
+
+#define BOLT_NUMBER 4
+#define BOLT_ITERATION 4
+#define LONG_FORK_ITERATION 3
+#define MEDIUM_FORK_ITERATION 2
+#define SMALL_FORK_ITERATION 1
+
+#define WIDTH_VARIATION 30
+#define HEIGHT_VARIATION 15
+
+#define DELAY_TIME_AMOUNT 15
+#define MULTI_DELAY_TIME_BASE 5
+
+#define MAX_WIGGLES 16
+#define WIGGLE_BASE 8
+#define WIGGLE_AMOUNT 14
+
+#define RANDOM_FORK_PROBILITY   4
+
+#define FIRST_LEVEL_STRIKE 0
+#define LEVEL_ONE_STRIKE 1
+#define LEVEL_TWO_STRIKE 2
+
+#define BOLT_VERTICIES ((1<<BOLT_ITERATION)-1)
+  /* BOLT_ITERATION = 4. 2^(BOLT_ITERATION) - 1 = 15 */
+
+#define NUMBER_FORK_VERTICIES 9
+
+#define FLASH_PROBILITY 20
+#define MAX_FLASH_AMOUNT 2     /*  half the total duration of the bolt  */
+
+typedef struct {
+       XPoint      ForkVerticies[NUMBER_FORK_VERTICIES];
+       int         num_used;
+} Fork;
+
+typedef struct {
+       XPoint      end1, end2;
+       XPoint      middle[BOLT_VERTICIES];
+       int         fork_number;
+       int         forks_start[2];
+       Fork        branch[2];
+       int         wiggle_number;
+       int         wiggle_amount;
+       int         delay_time;
+       int         flash;
+       int         flash_begin, flash_stop;
+       int         visible;
+       int         strike_level;
+} Lightning;
+
+typedef struct {
+       Lightning   bolts[BOLT_NUMBER];
+       int         scr_width, scr_height;
+       int         multi_strike;
+       int         give_it_hell;
+       int         draw_time;
+       int         stage;
+       unsigned long color;
+} Storm;
+
+static Storm *Helga = NULL;
+
+/*-------------------   function prototypes  ----------------------------*/
+
+static int  distance(XPoint a, XPoint b);
+
+static int  setup_multi_strike(void);
+static int  flashing_strike(void);
+static void flash_duration(int *start, int *end, int total_duration);
+static void random_storm(Storm * st);
+static void generate(XPoint A, XPoint B, int iter, XPoint * verts, int *index);
+static void create_fork(Fork * f, XPoint start, XPoint end, int level);
+
+static void first_strike(Lightning bolt, ModeInfo * mi);
+static void draw_bolt(Lightning * bolt, ModeInfo * mi);
+static void draw_line(ModeInfo * mi, XPoint * p, int number, GC use, int x_offset);
+static void level1_strike(Lightning bolt, ModeInfo * mi);
+static void level2_strike(Lightning bolt, ModeInfo * mi);
+
+static int  storm_active(Storm * st);
+static void update_bolt(Lightning * bolt, int time);
+static void wiggle_bolt(Lightning * bolt);
+static void wiggle_line(XPoint * p, int number, int wiggle_amount);
+
+/*-------------------------  functions  ---------------------------------*/
+
+static int
+setup_multi_strike(void)
+{
+       int         result, multi_prob;
+
+       multi_prob = NRAND(100);
+
+       if (multi_prob < 50)
+               result = 1;
+       else if ((multi_prob >= 51) && (multi_prob < 75))
+               result = 2;
+       else if ((multi_prob >= 76) && (multi_prob < 92))
+               result = 3;
+       else
+               result = BOLT_NUMBER;   /* 4 */
+
+       return (result);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+flashing_strike(void)
+{
+       int         tmp = NRAND(FLASH_PROBILITY);
+
+       if (tmp <= FLASH_PROBILITY)
+               return (1);
+       return (0);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+flash_duration(int *start, int *end, int total_duration)
+{
+       int         mid, d;
+
+       mid = total_duration / MAX_FLASH_AMOUNT;
+       d = NRAND(total_duration / MAX_FLASH_AMOUNT) / 2;
+       *start = mid - d;
+       *end = mid + d;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+random_storm(Storm * st)
+{
+       int         i, j, tmp;
+       XPoint      p;
+
+       for (i = 0; i < st->multi_strike; i++) {
+               st->bolts[i].end1.x = NRAND(st->scr_width);
+               st->bolts[i].end1.y = 0;
+               st->bolts[i].end2.x = NRAND(st->scr_width);
+               st->bolts[i].end2.y = st->scr_height;
+               st->bolts[i].wiggle_number = WIGGLE_BASE + NRAND(MAX_WIGGLES);
+               if ((st->bolts[i].flash = flashing_strike()))
+                       flash_duration(&(st->bolts[i].flash_begin), &(st->bolts[i].flash_stop),
+                                      st->bolts[i].wiggle_number);
+               else
+                       st->bolts[i].flash_begin = st->bolts[i].flash_stop = 0;
+               st->bolts[i].wiggle_amount = WIGGLE_AMOUNT;
+               if (i == 0)
+                       st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT);
+               else
+                       st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT) +
+                               (MULTI_DELAY_TIME_BASE * i);
+               st->bolts[i].strike_level = FIRST_LEVEL_STRIKE;
+               tmp = 0;
+               generate(st->bolts[i].end1, st->bolts[i].end2, BOLT_ITERATION,
+                        st->bolts[i].middle, &tmp);
+               st->bolts[i].fork_number = 0;
+               st->bolts[i].visible = 0;
+               for (j = 0; j < BOLT_VERTICIES; j++) {
+                       if (st->bolts[i].fork_number >= 2)
+                               break;
+                       if (NRAND(100) < RANDOM_FORK_PROBILITY) {
+                               p.x = NRAND(st->scr_width);
+                               p.y = st->scr_height;
+                               st->bolts[i].forks_start[st->bolts[i].fork_number] = j;
+                               create_fork(&(st->bolts[i].branch[st->bolts[i].fork_number]),
+                                           st->bolts[i].middle[j], p, j);
+                               st->bolts[i].fork_number++;
+                       }
+               }
+       }
+}
+
+static void
+generate(XPoint A, XPoint B, int iter, XPoint * verts, int *index)
+{
+       XPoint      mid;
+
+       mid.x = (A.x + B.x) / 2 + NRAND(WIDTH_VARIATION) - WIDTH_VARIATION / 2;
+       mid.y = (A.y + B.y) / 2 + NRAND(HEIGHT_VARIATION) - HEIGHT_VARIATION / 2;
+
+       if (!iter) {
+               verts[*index].x = mid.x;
+               verts[*index].y = mid.y;
+               (*index)++;
+               return;
+       }
+       generate(A, mid, iter - 1, verts, index);
+       generate(mid, B, iter - 1, verts, index);
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+create_fork(Fork * f, XPoint start, XPoint end, int level)
+{
+       int         tmp = 1;
+
+       f->ForkVerticies[0].x = start.x;
+       f->ForkVerticies[0].y = start.y;
+
+       if (level <= 6) {
+               generate(start, end, LONG_FORK_ITERATION, f->ForkVerticies, &tmp);
+               f->num_used = 9;
+       } else if ((level > 6) && (level <= 11)) {
+               generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
+               f->num_used = 5;
+       } else {
+               if (distance(start, end) > 100) {
+                       generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
+                       f->num_used = 5;
+               } else {
+                       generate(start, end, SMALL_FORK_ITERATION, f->ForkVerticies, &tmp);
+                       f->num_used = 3;
+               }
+       }
+
+       f->ForkVerticies[f->num_used - 1].x = end.x;
+       f->ForkVerticies[f->num_used - 1].y = end.y;
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+update_bolt(Lightning * bolt, int time)
+{
+       wiggle_bolt(bolt);
+       if ((bolt->wiggle_amount == 0) && (bolt->wiggle_number > 2))
+               bolt->wiggle_number = 0;
+       if (((time % 3) == 0))
+               bolt->wiggle_amount++;
+
+       if (((time >= bolt->delay_time) && (time < bolt->flash_begin)) ||
+           (time > bolt->flash_stop))
+               bolt->visible = 1;
+       else
+               bolt->visible = 0;
+
+       if (time == bolt->delay_time)
+               bolt->strike_level = FIRST_LEVEL_STRIKE;
+       else if (time == (bolt->delay_time + 1))
+               bolt->strike_level = LEVEL_ONE_STRIKE;
+       else if ((time > (bolt->delay_time + 1)) &&
+                (time <= (bolt->delay_time + bolt->flash_begin - 2)))
+               bolt->strike_level = LEVEL_TWO_STRIKE;
+       else if (time == (bolt->delay_time + bolt->flash_begin - 1))
+               bolt->strike_level = LEVEL_ONE_STRIKE;
+       else if (time == (bolt->delay_time + bolt->flash_stop + 1))
+               bolt->strike_level = LEVEL_ONE_STRIKE;
+       else
+               bolt->strike_level = LEVEL_TWO_STRIKE;
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+draw_bolt(Lightning * bolt, ModeInfo * mi)
+{
+       if (bolt->visible) {
+               if (bolt->strike_level == FIRST_LEVEL_STRIKE)
+                       first_strike(*bolt, mi);
+               else if (bolt->strike_level == LEVEL_ONE_STRIKE)
+                       level1_strike(*bolt, mi);
+               else
+                       level2_strike(*bolt, mi);
+       }
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+first_strike(Lightning bolt, ModeInfo * mi)
+{
+       Display    *display = MI_DISPLAY(mi);
+       Window      window = MI_WINDOW(mi);
+       GC          gc = MI_GC(mi);
+       int         i;
+
+       XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
+       XDrawLine(display, window, gc,
+              bolt.end1.x, bolt.end1.y, bolt.middle[0].x, bolt.middle[0].y);
+       draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 0);
+       XDrawLine(display, window, gc,
+       bolt.middle[BOLT_VERTICIES - 1].x, bolt.middle[BOLT_VERTICIES - 1].y,
+                 bolt.end2.x, bolt.end2.y);
+
+       for (i = 0; i < bolt.fork_number; i++)
+               draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
+                         gc, 0);
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+draw_line(ModeInfo * mi, XPoint * points, int number, GC to_use, int offset)
+{
+       int         i;
+
+       for (i = 0; i < number - 1; i++) {
+               if (points[i].y <= points[i + 1].y)
+                       XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x + offset,
+                                 points[i].y, points[i + 1].x + offset, points[i + 1].y);
+               else {
+                       if (points[i].x < points[i + 1].x)
+                               XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x +
+                                         offset, points[i].y + offset, points[i + 1].x + offset,
+                                         points[i + 1].y + offset);
+                       else
+                               XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x -
+                                         offset, points[i].y + offset, points[i + 1].x - offset,
+                                         points[i + 1].y + offset);
+               }
+       }
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+level1_strike(Lightning bolt, ModeInfo * mi)
+{
+       Display    *display = MI_DISPLAY(mi);
+       Window      window = MI_WINDOW(mi);
+       Storm      *st = &Helga[MI_SCREEN(mi)];
+       GC          gc = MI_GC(mi);
+       int         i;
+
+       if (MI_NPIXELS(mi) > 2) /* color */
+               XSetForeground(display, gc, MI_PIXEL(mi, st->color));
+       else
+               XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
+       XDrawLine(display, window, gc,
+       bolt.end1.x - 1, bolt.end1.y, bolt.middle[0].x - 1, bolt.middle[0].y);
+       draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -1);
+       XDrawLine(display, window, gc,
+                 bolt.middle[BOLT_VERTICIES - 1].x - 1,
+           bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 1, bolt.end2.y);
+       XDrawLine(display, window, gc,
+       bolt.end1.x + 1, bolt.end1.y, bolt.middle[0].x + 1, bolt.middle[0].y);
+       draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 1);
+       XDrawLine(display, window, gc,
+                 bolt.middle[BOLT_VERTICIES - 1].x + 1,
+           bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 1, bolt.end2.y);
+
+       for (i = 0; i < bolt.fork_number; i++) {
+               draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
+                         gc, -1);
+               draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
+                         gc, 1);
+       }
+       first_strike(bolt, mi);
+}
+
+/*------------------------------------------------------------------------*/
+
+static int
+distance(XPoint a, XPoint b)
+{
+       return ((int) sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+level2_strike(Lightning bolt, ModeInfo * mi)
+{
+       Display    *display = MI_DISPLAY(mi);
+       Window      window = MI_WINDOW(mi);
+       Storm      *st = &Helga[MI_SCREEN(mi)];
+       GC          gc = MI_GC(mi);
+       int         i;
+
+       /* This was originally designed to be a little darker then the
+          level1 strike.  This was changed to get it to work on
+          multiscreens and to add more color variety.   I tried
+          stippling but it did not look good. */
+       if (MI_NPIXELS(mi) > 2)
+               XSetForeground(display, gc, MI_PIXEL(mi, st->color));
+       else
+               XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
+       XDrawLine(display, window, gc,
+       bolt.end1.x - 2, bolt.end1.y, bolt.middle[0].x - 2, bolt.middle[0].y);
+       draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -2);
+       XDrawLine(display, window, gc,
+                 bolt.middle[BOLT_VERTICIES - 1].x - 2,
+           bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 2, bolt.end2.y);
+
+       XDrawLine(display, window, gc,
+       bolt.end1.x + 2, bolt.end1.y, bolt.middle[0].x + 2, bolt.middle[0].y);
+       draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 2);
+       XDrawLine(display, window, gc,
+                 bolt.middle[BOLT_VERTICIES - 1].x + 2,
+           bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 2, bolt.end2.y);
+
+       for (i = 0; i < bolt.fork_number; i++) {
+               draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
+                         gc, -2);
+               draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
+                         gc, 2);
+       }
+       level1_strike(bolt, mi);
+}
+
+/*------------------------------------------------------------------------*/
+
+static int
+storm_active(Storm * st)
+{
+       int         i, atleast_1 = 0;
+
+       for (i = 0; i < st->multi_strike; i++)
+               if (st->bolts[i].wiggle_number > 0)
+                       atleast_1++;
+
+       return (atleast_1);
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+wiggle_bolt(Lightning * bolt)
+{
+       int         i;
+
+       wiggle_line(bolt->middle, BOLT_VERTICIES, bolt->wiggle_amount);
+       bolt->end2.x += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2;
+       bolt->end2.y += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2;
+
+       for (i = 0; i < bolt->fork_number; i++) {
+               wiggle_line(bolt->branch[i].ForkVerticies, bolt->branch[i].num_used,
+                           bolt->wiggle_amount);
+               bolt->branch[i].ForkVerticies[0].x = bolt->middle[bolt->forks_start[i]].x;
+               bolt->branch[i].ForkVerticies[0].y = bolt->middle[bolt->forks_start[i]].y;
+       }
+
+       if (bolt->wiggle_amount > 1)
+               bolt->wiggle_amount -= 1;
+       else
+               bolt->wiggle_amount = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+static void
+wiggle_line(XPoint * p, int number, int amount)
+{
+       int         i;
+
+       for (i = 0; i < number; i++) {
+               p[i].x += NRAND(amount) - amount / 2;
+               p[i].y += NRAND(amount) - amount / 2;
+       }
+}
+
+/*------------------------------------------------------------------------*/
+
+void
+init_lightning(ModeInfo * mi)
+{
+       Storm      *st;
+
+       if (Helga == NULL) {
+               if ((Helga = (Storm *) calloc(MI_NUM_SCREENS(mi),
+                                             sizeof (Storm))) == NULL)
+                       return;
+       }
+       st = &Helga[MI_SCREEN(mi)];
+
+       st->scr_width = MI_WIN_WIDTH(mi);
+       st->scr_height = MI_WIN_HEIGHT(mi);
+
+       st->multi_strike = setup_multi_strike();
+       random_storm(st);
+       st->stage = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+void
+draw_lightning(ModeInfo * mi)
+{
+       Storm      *st = &Helga[MI_SCREEN(mi)];
+       int         i;
+
+       switch (st->stage) {
+               case 0:
+                       XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
+                       st->color = NRAND(MI_NPIXELS(mi));
+                       st->draw_time = 0;
+                       if (storm_active(st))
+                               st->stage++;
+                       else
+                               st->stage = 3;
+                       break;
+               case 1:
+                       for (i = 0; i < st->multi_strike; i++) {
+                               if (st->bolts[i].visible)
+                                       draw_bolt(&(st->bolts[i]), mi);
+                               update_bolt(&(st->bolts[i]), st->draw_time);
+                       }
+                       st->draw_time++;
+                       XFlush(MI_DISPLAY(mi));
+                       MI_PAUSE(mi) = 60000;
+                       st->stage++;
+                       break;
+               case 2:
+                       XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
+                       if (storm_active(st))
+                               st->stage = 1;
+                       else
+                               st->stage++;
+                       break;
+               case 3:
+                       MI_PAUSE(mi) = 1000000;
+                       init_lightning(mi);
+                       break;
+       }
+}
+
+void
+release_lightning(ModeInfo * mi)
+{
+       if (Helga != NULL) {
+               (void) free((void *) Helga);
+               Helga = NULL;
+       }
+}
+
+void
+refresh_lightning(ModeInfo * mi)
+{
+       /* Do nothing, it will refresh by itself */
+}