ftp://ftp.smr.ru/pub/0/FreeBSD/releases/distfiles/xscreensaver-3.16.tar.gz
[xscreensaver] / hacks / kaleidescope.c
diff --git a/hacks/kaleidescope.c b/hacks/kaleidescope.c
new file mode 100644 (file)
index 0000000..6e66fdd
--- /dev/null
@@ -0,0 +1,454 @@
+/* kaleidescope, Copyright (c) 1997 Ron Tapia <tapia@nmia.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty. 
+ */
+
+/*
+ * The above, for lack of a better copyright statement in easy reach
+ * was just lifted from the xscreensaver source.
+ *
+ * One of the odd things about this hack is that the radial motion of the
+ * segments depends on roundoff error alone. 
+ *
+ * I tried to make the source easy to add other shapes. So far, I've
+ * only messed with elipses and I couldn't do much with them that looked
+ * cool. A nice addition would be to add some sort of spline based shapes.
+ * Maybe rectangles would look nice.
+ *
+ */
+
+
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <X11/Xlib.h>
+#include "spline.h"
+#include "screenhack.h"
+
+#define NEWX(x,y) ((x*g.costheta) + (y*g.sintheta))
+#define NEWY(x,y) ((y*g.costheta) - (x*g.sintheta))
+
+
+typedef struct {
+  int  xoff, yoff;                    /* offset of origin xmax/2, ymax/2 */
+  int  xmax, ymax;                    /* width, height of window */
+  float costheta, sintheta;           
+  int symmetry;                       
+  int ntrails;          
+  int nsegments;
+  int narcs;
+  int nobjects;
+  int local_rotation;
+  int global_rotation;
+  int spring_constant;
+  Colormap cmap;
+  GC  draw_gc;
+  GC  erase_gc;
+  unsigned int default_fg_pixel;
+  Display *dpy;
+  Window window;
+  unsigned long delay;
+  unsigned short redmin,redrange,greenmin,greenrange,bluemin,bluerange;
+  int color_mode;
+} GLOBAL;
+
+typedef struct Obj OBJECT;
+struct Obj {
+  int type;
+  int time;
+  void (*propogate) (OBJECT *);
+  void (*draw) (OBJECT *);
+  void (*init) (OBJECT *);
+  void *cur;
+};
+
+typedef struct KSEGMENT {
+  struct KSEGMENT *next;
+  XColor color;
+  int drawn;
+  short int x1,y1,x2,y2;  /* these are in the natural coordinate system */
+  XSegment  *xsegments;  /* these are in the X coordinate system */
+} Ksegment;
+
+/* BEGIN global variables */
+
+GLOBAL g;
+OBJECT *objects;
+
+char *progclass = "Kaleidescope";
+char *defaults [] = {
+  ".background:             black",
+  ".foreground:             white",
+  "*color_mode:      nice",
+  "*symmetry:         11",
+  "*ntrails:         100",
+  "*nsegments:          7",
+  "*local_rotation:   -59",
+  "*global_rotation:    1",
+  "*spring_constant:    5",
+  "*delay:          20000",
+  "*redmin:         30000",
+  "*redrange:       20000",
+  "*greenmin:       30000",
+  "*greenrange:     20000",
+  "*bluemin:        30000",
+  "*bluerange:      20000",
+  0
+};
+
+XrmOptionDescRec options [] = {
+  { "-color_mode",       ".color_mode",     XrmoptionSepArg, 0 },
+  { "-symmetry",       ".symmetry",        XrmoptionSepArg, 0 },
+  { "-nsegments",       ".nsegments",       XrmoptionSepArg, 0 },
+  { "-ntrails",                ".ntrails",         XrmoptionSepArg, 0 },
+  { "-local_rotation",  ".local_rotation",  XrmoptionSepArg, 0 },
+  { "-global_rotation", ".global_rotation", XrmoptionSepArg, 0 },
+  { "-delay",           ".delay",           XrmoptionSepArg, 0 },
+  { "-spring_constant", ".spring_constant", XrmoptionSepArg, 0 },
+  { "-redmin",          ".redmin",          XrmoptionSepArg, 0 },
+  { "-redrange",        ".redmin",          XrmoptionSepArg, 0 },
+  { "-bluemin",         ".bluemin",         XrmoptionSepArg, 0 },
+  { "-bluerange",       ".bluerange",       XrmoptionSepArg, 0 },
+  { "-greenmin",        ".greenmin",        XrmoptionSepArg, 0 },
+  { "-greenrange",      ".greenrange",      XrmoptionSepArg, 0 },
+  { 0, 0, 0, 0 }
+};
+
+/* END global variables */
+
+static void
+krandom_color(XColor *color)
+{
+  int r;
+  r = random() % 3;
+
+  if((g.color_mode == 0) || (g.color_mode == 1)) {
+
+    color->blue  = ((r = random()) % g.bluerange) + g.bluemin;
+    color->green = ((r = random()) % g.greenrange) + g.greenmin;
+    color->red   = ((r = random()) % g.redrange) + g.redmin;
+
+    if(!XAllocColor(g.dpy, g.cmap, color)) {
+      color->pixel = g.default_fg_pixel;
+    } 
+    return;
+  } else {
+    color->pixel = g.default_fg_pixel;
+    return;
+  }
+}
+
+
+static void
+kcopy_color(XColor *to, XColor *from)
+{
+  to->red   = from->red;
+  to->green = from->green;
+  to->blue  = from->blue;
+  to->pixel = from->pixel;
+}
+
+static void
+kcycle_color(XColor *color,
+            unsigned short redstep,
+            unsigned short greenstep,
+            unsigned short bluestep) 
+{
+  unsigned short red,green,blue;
+
+  if (! g.color_mode) {
+    XColor copy;
+    color->flags = DoRed|DoGreen|DoBlue;
+    color->red   = (red = color->red) - redstep;
+    color->green = (green = color->green) - greenstep;
+    color->blue  = (blue = color->blue)  - bluestep;
+    copy = *color;
+
+    if(!XAllocColor(g.dpy, g.cmap, color)) {
+      /* printf("couldn't alloc color...\n"); */
+      color->pixel = g.default_fg_pixel;
+    }
+    copy.pixel = color->pixel;
+    *color = copy;
+
+    color->red   = red   - redstep;
+    color->green = green- greenstep;
+    color->blue  = blue  - bluestep;
+    return;
+  } 
+}
+
+
+static Ksegment *
+create_ksegment (void)
+{
+  Ksegment *seg, *prev;
+  XColor new_color;
+  int i;
+  unsigned short redstep,bluestep,greenstep;
+
+  krandom_color(&new_color);
+
+  redstep = new_color.red/(2 * g.ntrails);
+  greenstep = new_color.green/(2 * g.ntrails);
+  bluestep = new_color.blue/(2 * g.ntrails);
+
+  seg            = (Ksegment *) malloc(sizeof(Ksegment));
+  seg->xsegments = (XSegment  *) malloc(g.symmetry * sizeof(XSegment));
+
+  prev = seg;
+  for(i=0; i< (g.ntrails - 1); i++) {
+
+    kcycle_color(&new_color,redstep,greenstep,bluestep);
+
+    kcopy_color(&(prev->color), &new_color);
+
+    prev->next              = (Ksegment*)malloc(sizeof(Ksegment));
+    (prev->next)->xsegments = (XSegment*)malloc(g.symmetry * sizeof(XSegment));
+    prev->drawn             = 0;
+    prev = (prev->next);
+  } 
+
+  prev->drawn = 0;
+  prev->next = seg;
+  kcopy_color(&(prev->color), &new_color);
+
+  return seg;
+}
+
+static void
+init_ksegment (OBJECT *obj)
+{
+
+  /* Give the segment some random values */
+  ((Ksegment *)obj->cur)->x1 = random() % g.xoff;
+  ((Ksegment *)obj->cur)->y1 = random() % g.yoff;
+  ((Ksegment *)obj->cur)->x2 = random() % g.xoff;
+  ((Ksegment *)obj->cur)->y2 = random() % g.yoff;
+}
+
+
+static void
+draw_ksegment (OBJECT *obj)
+{
+  register short x1, y1, x2, y2;
+  int dx, dy;
+  int i;
+  static int counter=0;
+
+  counter++;
+
+  x1 = ((Ksegment *)obj->cur)->x1;   /* in the natural coordinate system */
+  y1 = ((Ksegment *)obj->cur)->y1;
+  x2 = ((Ksegment *)obj->cur)->x2;
+  y2 = ((Ksegment *)obj->cur)->y2;
+
+  dx = x2 - x1;
+  dy = y2 - y1;
+
+  /* maybe throw away values and start over */
+  if( ((dx*dx) + (dy * dy)) < 100) {
+    init_ksegment (obj);
+    x1 = ((Ksegment *)obj->cur)->x1;   /* in the natural coordinate system */
+    y1 = ((Ksegment *)obj->cur)->y1;
+    x2 = ((Ksegment *)obj->cur)->x2;
+    y2 = ((Ksegment *)obj->cur)->y2;
+  }
+
+  for (i=0; i<g.symmetry; i++) {
+    (((Ksegment *)obj->cur)->xsegments)[i].x1 = NEWX(x1,y1);
+    (((Ksegment *)obj->cur)->xsegments)[i].y1 = NEWY(x1,y1);
+    (((Ksegment *)obj->cur)->xsegments)[i].x2 = NEWX(x2,y2);
+    (((Ksegment *)obj->cur)->xsegments)[i].y2 = NEWY(x2,y2);
+
+    (((Ksegment *)obj->cur)->xsegments)[i].x1 = (x1 = (((Ksegment *)obj->cur)->xsegments)[i].x1) + g.xoff; 
+    (((Ksegment *)obj->cur)->xsegments)[i].y1 = (y1 = (((Ksegment *)obj->cur)->xsegments)[i].y1) + g.yoff;
+    (((Ksegment *)obj->cur)->xsegments)[i].x2 = (x2 = (((Ksegment *)obj->cur)->xsegments)[i].x2) + g.xoff;
+    (((Ksegment *)obj->cur)->xsegments)[i].y2 = (y2 = (((Ksegment *)obj->cur)->xsegments)[i].y2) + g.yoff;
+  }
+
+  XSetForeground(g.dpy, g.draw_gc, (((Ksegment *)obj->cur)->color).pixel);
+
+  XDrawSegments(g.dpy, g.window, g.draw_gc, ((Ksegment *)obj->cur)->xsegments, g.symmetry);
+  ((Ksegment *)obj->cur)->drawn = 1;
+
+  if (((((Ksegment *)obj->cur)->next)->drawn) != 0) {
+    XDrawSegments(g.dpy, g.window, g.erase_gc, ((Ksegment *)obj->cur)->next->xsegments, g.symmetry);
+  }
+}
+
+static void
+propogate_ksegment(OBJECT *obj)
+{
+  int t;
+  short int x1,y1,x2,y2;
+  short int midx,midy,nmidx,nmidy;
+  float lsin, lcos, gsin, gcos;
+
+  lsin = sin((2*M_PI/10000)*g.local_rotation);
+  lcos = cos((2*M_PI/10000)*g.local_rotation);
+  gsin = sin((2*M_PI/10000)*g.global_rotation);
+  gcos = cos((2*M_PI/10000)*g.global_rotation);
+
+  t=obj->time;
+  obj->time = t + 1;
+
+  x1 = ((Ksegment *) obj->cur)->x1;
+  y1 = ((Ksegment *) obj->cur)->y1;
+  x2 = ((Ksegment *) obj->cur)->x2;
+  y2 = ((Ksegment *) obj->cur)->y2;
+
+  midx = (x1 + x2)/2;
+  midy = (y1 + y2)/2;
+
+  nmidx = midx*gcos + midy*gsin;
+  nmidy = midy*gcos - midx*gsin;
+
+  x1 = x1 - midx;
+  x2 = x2 - midx;
+  y1 = y1 - midy;
+  y2 = y2 - midy;
+
+
+  /* This is where we move to the next ksegment... */
+  obj->cur = ((Ksegment *)obj->cur)->next;
+
+  ((Ksegment *)obj->cur)->x1 = ((x1*lcos) + (y1*lsin)) + nmidx;
+  ((Ksegment *)obj->cur)->y1 = ((y1*lcos) - (x1*lsin)) + nmidy;
+  ((Ksegment *)obj->cur)->x2 = ((x2*lcos) + (y2*lsin)) + nmidx;
+  ((Ksegment *)obj->cur)->y2 = ((y2*lcos) - (x2*lsin)) + nmidy;
+
+  return ;
+}
+
+static void
+init_objects (void)
+{
+  int i;
+  for (i=0; i<g.nobjects; i++) {
+    (objects[i].init)(objects + i);
+  }
+}
+
+static void
+create_objects (void)
+{
+  int i;
+
+  objects = (OBJECT *) malloc(g.nobjects * sizeof(OBJECT));
+
+  for (i=0; i< g.nsegments; i++) {
+    objects[i].cur = create_ksegment();
+    objects[i].type = 1;
+    objects[i].time = 0;
+    objects[i].propogate = propogate_ksegment;
+    objects[i].draw      = draw_ksegment;
+    objects[i].init      = init_ksegment;
+  }
+
+  /* Here we can add creation functions for other object types. */
+}
+
+
+static void
+propogate_objects (void)
+{
+  int i;
+
+  for(i=0; i<g.nobjects; i++) {
+    objects[i].propogate(objects + i);
+  }
+}
+
+static void
+draw_objects (void)
+{
+  int i;
+
+  for(i=0; i<g.nobjects; i++) {
+    objects[i].draw(objects + i);
+  }
+}
+
+static void
+init_g (Display *dpy, Window window)
+{
+  XWindowAttributes xgwa;
+  XGCValues gcv;
+  char *color_mode_str;
+
+  g.dpy    = dpy;
+  g.window = window;
+
+  g.symmetry        = get_integer_resource("symmetry",         "Integer");
+  g.ntrails         = get_integer_resource("ntrails"  ,        "Integer");
+  g.nsegments       = get_integer_resource("nsegments",        "Integer");
+  g.narcs           = get_integer_resource("narcs",            "Integer");
+  g.local_rotation  = get_integer_resource("local_rotation",   "Integer");
+  g.global_rotation = get_integer_resource("global_rotation",  "Integer");
+  g.spring_constant = get_integer_resource("sprint_constatnt", "Integer");
+  g.delay           = get_integer_resource("delay", "Integer");
+  g.nobjects        = g.nsegments + g.narcs;
+
+  color_mode_str = get_string_resource("color_mode", "color_mode");
+
+  /* make into an enum... */
+  if(!color_mode_str) {
+    g.color_mode = 0;
+  } else if (!strcmp(color_mode_str, "greedy")) {
+    g.color_mode = 0;
+  } else if (!strcmp(color_mode_str, "nice")) {
+    g.color_mode = 1;
+  } else {
+    g.color_mode = 2;
+  }
+
+  XGetWindowAttributes (dpy, (Drawable) window, &xgwa);
+  g.xmax     = xgwa.width;
+  g.ymax     = xgwa.height;  
+  g.xoff     = g.xmax/2;
+  g.yoff     = g.ymax/2;
+  g.costheta = cos(2*M_PI/g.symmetry);
+  g.sintheta  = sin(2*M_PI/g.symmetry);
+  g.cmap     = xgwa.colormap;
+
+  g.redmin     = get_integer_resource("redmin",     "Integer");
+  g.redrange   = get_integer_resource("redrange",   "Integer");
+  g.greenmin   = get_integer_resource("greenmin",   "Integer");
+  g.greenrange = get_integer_resource("greenrange", "Integer");
+  g.bluemin    = get_integer_resource("bluemin",    "Integer");
+  g.bluerange  = get_integer_resource("bluerange",  "Integer");
+
+  gcv.line_width = 1;
+  gcv.cap_style  = CapRound;
+  gcv.foreground = g.default_fg_pixel = get_pixel_resource ("foreground", "Foreground", dpy, g.cmap);
+  g.draw_gc      = XCreateGC (dpy, (Drawable) window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
+
+  gcv.foreground = get_pixel_resource ("background", "Background", g.dpy, g.cmap);
+  g.erase_gc     = XCreateGC (dpy, (Drawable) window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
+}
+
+void
+screenhack (Display *dpy, Window window)
+{
+  init_g (dpy, window);
+  create_objects();
+  init_objects ();
+
+  while (1)
+    {
+     draw_objects ();
+     XSync (dpy, False);
+     if(g.delay) {
+       screenhack_handle_events (dpy);
+       usleep(g.delay);
+     }
+     propogate_objects(); 
+   }
+}