diff options
Diffstat (limited to 'hacks/ccurve.c')
-rw-r--r-- | hacks/ccurve.c | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/hacks/ccurve.c b/hacks/ccurve.c new file mode 100644 index 0000000..23b53e8 --- /dev/null +++ b/hacks/ccurve.c @@ -0,0 +1,866 @@ +/* ccurve, Copyright (c) 1998, 1999 + * Rick Campbell <rick@campbellcentral.org> + * + * 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. + * + */ + +/* Draw self-similar linear fractals including the classic ``C Curve'' + * + * 16 Aug 1999 Rick Campbell <rick@campbellcentral.org> + * Eliminated sub-windows-with-backing-store-double-buffering crap in + * favor of drawing the new image in a pixmap and then splatting that on + * the window. + * + * 19 Dec 1998 Rick Campbell <rick@campbellcentral.org> + * Original version. + */ + +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include "screenhack.h" +#include "colors.h" +#include "erase.h" + +#define SQRT3 (1.73205080756887729353) +#define MAXIMUM_COLOR_COUNT (256) +#define EPSILON (1e-5) + +typedef struct Position_struct +{ + double x; + double y; +} +Position; + +typedef struct Segment_struct +{ + double angle; + double length; +} +Segment; + +struct state { + Display *dpy; + Window window; + + int color_count; + int color_index; + Colormap color_map; + XColor colors [MAXIMUM_COLOR_COUNT]; + int line_count; + int maximum_lines; + double plot_maximum_x; + double plot_maximum_y; + double plot_minimum_x; + double plot_minimum_y; + int total_lines; + + unsigned long int background; + GC context; + Pixmap pixmap; + int width; + int height; + float delay; + float delay2; + + int draw_index; + + int draw_iterations; + double draw_maximum_x; + double draw_maximum_y; + double draw_minimum_x; + double draw_minimum_y; + int draw_segment_count; + Segment* draw_segments; + double draw_x1; + double draw_y1; + double draw_x2; + double draw_y2; +}; + + + + +/* normalize alters the sequence to go from (0,0) to (1,0) */ +static void +normalized_plot (int segment_count, + Segment* segments, + Position* points) +{ + double angle = 0.0; + double cosine = 0.0; + int index = 0; + double length = 0.0; + double sine = 0.0; + double x = 0.0; + double y = 0.0; + + for (index = 0; index < segment_count; ++index) + { + Segment* segment = segments + index; + double length = segment->length; + double angle = segment->angle; + + x += length * cos (angle); + y += length * sin (angle); + points [index].x = x; + points [index].y = y; + } + angle = -(atan2 (y, x)); + cosine = cos (angle); + sine = sin (angle); + length = sqrt ((x * x) + (y * y)); + /* rotate and scale */ + for (index = 0; index < segment_count; ++index) + { + double temp_x = points [index].x; + double temp_y = points [index].y; + points [index].x = ((temp_x * cosine) + (temp_y * (-sine))) / length; + points [index].y = ((temp_x * sine) + (temp_y * cosine)) / length; + } +} + +static void +copy_points (int segment_count, + Position* source, + Position* target) +{ + int index = 0; + + for (index = 0; index < segment_count; ++index) + { + target [index] = source [index]; + } +} + +static void +realign (double x1, + double y1, + double x2, + double y2, + int segment_count, + Position* points) +{ + double angle = 0.0; + double cosine = 0.0; + double delta_x = 0.0; + double delta_y = 0.0; + int index = 0; + double length = 0.0; + double sine = 0.0; + + delta_x = x2 - x1; + delta_y = y2 - y1; + angle = atan2 (delta_y, delta_x); + cosine = cos (angle); + sine = sin (angle); + length = sqrt ((delta_x * delta_x) + (delta_y * delta_y)); + /* rotate, scale, then shift */ + for (index = 0; index < segment_count; ++index) + { + double temp_x = points [index].x; + double temp_y = points [index].y; + points [index].x + = (length * ((temp_x * cosine) + (temp_y * (-sine)))) + x1; + points [index].y + = (length * ((temp_x * sine) + (temp_y * cosine))) + y1; + } +} + +static Bool +self_similar_normalized (struct state *st, + int iterations, + double x1, + double y1, + double x2, + double y2, + double maximum_x, + double maximum_y, + double minimum_x, + double minimum_y, + int segment_count, + Position* points) +{ + if (iterations == 0) + { + double delta_x = maximum_x - minimum_x; + double delta_y = maximum_y - minimum_y; + st->color_index = (int)(((double)(st->line_count * st->color_count)) + / ((double)st->total_lines)); + ++st->line_count; + XSetForeground (st->dpy, st->context, st->colors [st->color_index].pixel); + if (st->plot_maximum_x < x1) st->plot_maximum_x = x1; + if (st->plot_maximum_x < x2) st->plot_maximum_x = x2; + if (st->plot_maximum_y < y1) st->plot_maximum_y = y1; + if (st->plot_maximum_y < y2) st->plot_maximum_y = y2; + if (st->plot_minimum_x > x1) st->plot_minimum_x = x1; + if (st->plot_minimum_x > x2) st->plot_minimum_x = x2; + if (st->plot_minimum_y > y1) st->plot_minimum_y = y1; + if (st->plot_minimum_y > y2) st->plot_minimum_y = y2; + XDrawLine (st->dpy, st->pixmap, st->context, + (int)(((x1 - minimum_x) / delta_x) * st->width), + (int)(((maximum_y - y1) / delta_y) * st->height), + (int)(((x2 - minimum_x) / delta_x) * st->width), + (int)(((maximum_y - y2) / delta_y) * st->height)); + } + else + { + int index = 0; + double next_x = 0.0; + double next_y = 0.0; + Position* replacement = (Position*)NULL; + double x = 0.0; + double y = 0.0; + + replacement = (Position*)(malloc (segment_count * sizeof (Segment))); + copy_points (segment_count, points, replacement); + assert (fabs ((replacement [segment_count - 1].x) - 1.0) < EPSILON); + assert (fabs (replacement [segment_count - 1].y) < EPSILON); + realign (x1, y1, x2, y2, segment_count, replacement); + /* jwz: I don't understand what these assertions are supposed to + be detecting, but let's just bail on the fractal instead of + crashing. */ +/* assert (fabs (x2 - (replacement [segment_count - 1].x)) < EPSILON); + assert (fabs (y2 - (replacement [segment_count - 1].y)) < EPSILON);*/ + if (fabs (x2 - (replacement [segment_count - 1].x)) >= EPSILON || + fabs (y2 - (replacement [segment_count - 1].y)) >= EPSILON) { + free (replacement); + return False; + } + x = x1; + y = y1; + for (index = 0; index < segment_count; ++index) + { + next_x = replacement [index].x; + next_y = replacement [index].y; + if (!self_similar_normalized (st, + iterations - 1, x, y, next_x, next_y, + maximum_x, maximum_y, + minimum_x, minimum_y, + segment_count, points)) { + free(replacement); + return False; + } + x = next_x; + y = next_y; + } + free(replacement); + } + return True; +} + +static void +self_similar (struct state *st, + Pixmap pixmap, + GC context, + int width, + int height, + int iterations, + double x1, + double y1, + double x2, + double y2, + double maximum_x, + double maximum_y, + double minimum_x, + double minimum_y, + int segment_count, + Segment* segments) +{ + Position* points = (Position*)NULL; + + points = (Position*)(malloc (segment_count * sizeof (Position))); + normalized_plot (segment_count, segments, points); + assert (fabs ((points [segment_count - 1].x) - 1.0) < EPSILON); + assert (fabs (points [segment_count - 1].y) < EPSILON); + self_similar_normalized (st, iterations, + x1, y1, x2, y2, + maximum_x, maximum_y, + minimum_x, minimum_y, + segment_count, points); + free((void*)points); +} + +static +double +random_double (double base, + double limit, + double epsilon) +{ + double range = 0.0; + unsigned int steps = 0; + + assert (base < limit); + assert (epsilon > 0.0); + range = limit - base; + steps = (unsigned int)(floor (range / epsilon)); + return base + ((random () % steps) * epsilon); +} + +static void +select_2_pattern (Segment* segments) +{ + if ((random () % 2) == 0) + { + if ((random () % 2) == 0) + { + segments [0].angle = -M_PI_4; + segments [0].length = M_SQRT2; + segments [1].angle = M_PI_4; + segments [1].length = M_SQRT2; + } + else + { + segments [0].angle = M_PI_4; + segments [0].length = M_SQRT2; + segments [1].angle = -M_PI_4; + segments [1].length = M_SQRT2; + } + } + else + { + segments [0].angle + = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0); + segments [0].length = random_double (0.25, 0.67, 0.001); + if ((random () % 2) == 0) + { + segments [1].angle = -(segments [0].angle); + segments [1].length = segments [0].length; + } + else + { + segments [1].angle = random_double ((-M_PI) / 3.0, + (-M_PI) / 6.0, + M_PI / 180.0); + segments [1].length = random_double (0.25, 0.67, 0.001); + } + } +} + +static void +select_3_pattern (Segment* segments) +{ + switch (random () % 5) + { + case 0: + if ((random () % 2) == 0) + { + segments [0].angle = M_PI_4; + segments [0].length = M_SQRT2 / 4.0; + segments [1].angle = -M_PI_4; + segments [1].length = M_SQRT2 / 2.0; + segments [2].angle = M_PI_4; + segments [2].length = M_SQRT2 / 4.0; + } + else + { + segments [0].angle = -M_PI_4; + segments [0].length = M_SQRT2 / 4.0; + segments [1].angle = M_PI_4; + segments [1].length = M_SQRT2 / 2.0; + segments [2].angle = -M_PI_4; + segments [2].length = M_SQRT2 / 4.0; + } + break; + case 1: + if ((random () % 2) == 0) + { + segments [0].angle = M_PI / 6.0; + segments [0].length = 1.0; + segments [1].angle = -M_PI_2; + segments [1].length = 1.0; + segments [2].angle = M_PI / 6.0; + segments [2].length = 1.0; + } + else + { + segments [0].angle = -M_PI / 6.0; + segments [0].length = 1.0; + segments [1].angle = M_PI_2; + segments [1].length = 1.0; + segments [2].angle = -M_PI / 6.0; + segments [2].length = 1.0; + } + break; + case 2: + case 3: + case 4: + segments [0].angle + = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0); + segments [0].length = random_double (0.25, 0.67, 0.001); + segments [1].angle + = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0); + segments [1].length = random_double (0.25, 0.67, 0.001); + if ((random () % 3) == 0) + { + if ((random () % 2) == 0) + { + segments [2].angle = segments [0].angle; + } + else + { + segments [2].angle = -(segments [0].angle); + } + segments [2].length = segments [0].length; + } + else + { + segments [2].angle + = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0); + segments [2].length = random_double (0.25, 0.67, 0.001); + } + break; + } +} + +static void +select_4_pattern (Segment* segments) +{ + switch (random () % 9) + { + case 0: + if ((random () % 2) == 0) + { + double length = random_double (0.25, 0.50, 0.001); + + segments [0].angle = 0.0; + segments [0].length = 0.5; + segments [1].angle = M_PI_2; + segments [1].length = length; + segments [2].angle = -M_PI_2; + segments [2].length = length; + segments [3].angle = 0.0; + segments [3].length = 0.5; + } + else + { + double length = random_double (0.25, 0.50, 0.001); + + segments [0].angle = 0.0; + segments [0].length = 0.5; + segments [1].angle = -M_PI_2; + segments [1].length = length; + segments [2].angle = M_PI_2; + segments [2].length = length; + segments [3].angle = 0.0; + segments [3].length = 0.5; + } + break; + case 1: + if ((random () % 2) == 0) + { + segments [0].angle = 0.0; + segments [0].length = 0.5; + segments [1].angle = M_PI_2; + segments [1].length = 0.45; + segments [2].angle = -M_PI_2; + segments [2].length = 0.45; + segments [3].angle = 0.0; + segments [3].length = 0.5; + } + else + { + segments [0].angle = 0.0; + segments [0].length = 0.5; + segments [1].angle = -M_PI_2; + segments [1].length = 0.45; + segments [2].angle = M_PI_2; + segments [2].length = 0.45; + segments [3].angle = 0.0; + segments [3].length = 0.5; + } + break; + case 2: + if ((random () % 2) == 0) + { + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = (5.0 * M_PI) / 12.0; + segments [1].length = 1.2; + segments [2].angle = (-5.0 * M_PI) / 12.0; + segments [2].length = 1.2; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + else + { + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = (-5.0 * M_PI) / 12.0; + segments [1].length = 1.2; + segments [2].angle = (5.0 * M_PI) / 12.0; + segments [2].length = 1.2; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + break; + case 3: + if ((random () % 2) == 0) + { + double angle + = random_double (M_PI / 4.0, + M_PI_2, + M_PI / 180.0); + + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = angle; + segments [1].length = 1.2; + segments [2].angle = (-angle); + segments [2].length = 1.2; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + else + { + double angle + = random_double (M_PI / 4.0, + M_PI_2, + M_PI / 180.0); + + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = (-angle); + segments [1].length = 1.2; + segments [2].angle = angle; + segments [2].length = 1.2; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + break; + case 4: + if ((random () % 2) == 0) + { + double angle + = random_double (M_PI / 4.0, + M_PI_2, + M_PI / 180.0); + + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = angle; + segments [1].length = 1.2; + segments [2].angle = (-angle); + segments [2].length = 1.2; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + else + { + double angle + = random_double (M_PI / 4.0, + M_PI_2, + M_PI / 180.0); + + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = (-angle); + segments [1].length = 1.2; + segments [2].angle = angle; + segments [2].length = 1.2; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + break; + case 5: + if ((random () % 2) == 0) + { + double angle + = random_double (M_PI / 4.0, + M_PI_2, + M_PI / 180.0); + double length = random_double (0.25, 0.50, 0.001); + + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = angle; + segments [1].length = length; + segments [2].angle = (-angle); + segments [2].length = length; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + else + { + double angle + = random_double (M_PI / 4.0, + M_PI_2, + M_PI / 180.0); + double length = random_double (0.25, 0.50, 0.001); + + segments [0].angle = 0.0; + segments [0].length = 1.0; + segments [1].angle = (-angle); + segments [1].length = length; + segments [2].angle = angle; + segments [2].length = length; + segments [3].angle = 0.0; + segments [3].length = 1.0; + } + break; + case 6: + case 7: + case 8: + segments [0].angle + = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001); + segments [0].length = random_double (0.25, 0.50, 0.001); + segments [1].angle + = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001); + segments [1].length = random_double (0.25, 0.50, 0.001); + if ((random () % 3) == 0) + { + segments [2].angle + = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001); + segments [2].length = random_double (0.25, 0.50, 0.001); + segments [3].angle + = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001); + segments [3].length = random_double (0.25, 0.50, 0.001); + } + else + { + if ((random () % 2) == 0) + { + segments [2].angle = -(segments [1].angle); + segments [2].length = segments [1].length; + segments [3].angle = -(segments [0].angle); + segments [3].length = segments [0].length; + } + else + { + segments [2].angle = segments [1].angle; + segments [2].length = segments [1].length; + segments [3].angle = segments [0].angle; + segments [3].length = segments [0].length; + } + } + break; + } +} + +static void +select_pattern (int segment_count, + Segment* segments) +{ + switch (segment_count) + { + case 2: + select_2_pattern (segments); + break; + case 3: + select_3_pattern (segments); + break; + case 4: + select_4_pattern (segments); + break; + default: + fprintf (stderr, "\nBad segment count, must be 2, 3, or 4.\n"); + exit (1); + } +} + +#define Y_START (0.5) + +static void * +ccurve_init (Display *dpy, Window window) +{ + struct state *st = (struct state *) calloc (1, sizeof(*st)); + unsigned long int black = 0; + int depth = 0; + XWindowAttributes hack_attributes; + XGCValues values; + unsigned long int white = 0; + + st->dpy = dpy; + st->window = window; + + st->delay = get_float_resource (st->dpy, "delay", "Integer"); + st->delay2 = get_float_resource (st->dpy, "pause", "Integer"); + st->maximum_lines = get_integer_resource (st->dpy, "limit", "Integer"); + black = BlackPixel (st->dpy, DefaultScreen (st->dpy)); + white = WhitePixel (st->dpy, DefaultScreen (st->dpy)); + st->background = black; + XGetWindowAttributes (st->dpy, st->window, &hack_attributes); + st->width = hack_attributes.width; + st->height = hack_attributes.height; + depth = hack_attributes.depth; + st->color_map = hack_attributes.colormap; + st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height, depth); + values.foreground = white; + values.background = black; + st->context = XCreateGC (st->dpy, st->window, GCForeground | GCBackground, + &values); + st->color_count = MAXIMUM_COLOR_COUNT; + make_color_loop (hack_attributes.screen, hack_attributes.visual, + st->color_map, + 0, 1, 1, + 120, 1, 1, + 240, 1, 1, + st->colors, &st->color_count, True, False); + if (st->color_count <= 0) + { + st->color_count = 1; + st->colors [0].red = st->colors [0].green = st->colors [0].blue = 0xFFFF; + XAllocColor (st->dpy, st->color_map, &st->colors [0]); + } + + st->draw_maximum_x = 1.20; + st->draw_maximum_y = 0.525; + st->draw_minimum_x = -0.20; + st->draw_minimum_y = -0.525; + st->draw_x2 = 1.0; + + return st; +} + +static unsigned long +ccurve_draw (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + static const int lengths [] = { 4, 4, 4, 4, 4, 3, 3, 3, 2 }; + + if (st->draw_index == 0) + { + st->draw_segment_count + = lengths [random () % (sizeof (lengths) / sizeof (int))]; + st->draw_segments + = (Segment*)(malloc ((st->draw_segment_count) * sizeof (Segment))); + select_pattern (st->draw_segment_count, st->draw_segments); + st->draw_iterations = floor (log (st->maximum_lines) + / log (((double)(st->draw_segment_count)))); + if ((random () % 3) != 0) + { + double factor = 0.45; + st->draw_x1 += random_double (-factor, factor, 0.001); + st->draw_y1 += random_double (-factor, factor, 0.001); + st->draw_x2 += random_double (-factor, factor, 0.001); + st->draw_y2 += random_double (-factor, factor, 0.001); + } +/* background = (random () % 2) ? black : white; */ + + } + + /* for (st->draw_index = 0; st->draw_index < st->draw_iterations; ++st->draw_index) */ + { + double delta_x = 0.0; + double delta_y = 0.0; + + XSetForeground (st->dpy, st->context, st->background); + XFillRectangle (st->dpy, st->pixmap, st->context, 0, 0, st->width, st->height); + st->line_count = 0; + st->total_lines = (int)(pow ((double)(st->draw_segment_count), + (double)st->draw_index)); + st->plot_maximum_x = -1000.00; + st->plot_maximum_y = -1000.00; + st->plot_minimum_x = 1000.00; + st->plot_minimum_y = 1000.00; + self_similar (st, st->pixmap, st->context, st->width, st->height, st->draw_index, + st->draw_x1, st->draw_y1, st->draw_x2, st->draw_y2, + st->draw_maximum_x, + st->draw_maximum_y, + st->draw_minimum_x, + st->draw_minimum_y, + st->draw_segment_count, st->draw_segments); + delta_x = st->plot_maximum_x - st->plot_minimum_x; + delta_y = st->plot_maximum_y - st->plot_minimum_y; + st->draw_maximum_x = st->plot_maximum_x + (delta_x * 0.2); + st->draw_maximum_y = st->plot_maximum_y + (delta_y * 0.2); + st->draw_minimum_x = st->plot_minimum_x - (delta_x * 0.2); + st->draw_minimum_y = st->plot_minimum_y - (delta_y * 0.2); + delta_x = st->draw_maximum_x - st->draw_minimum_x; + delta_y = st->draw_maximum_y - st->draw_minimum_y; + if ((delta_y / delta_x) > (((double)st->height) / ((double)st->width))) + { + double new_delta_x + = (delta_y * ((double)st->width)) / ((double)st->height); + st->draw_minimum_x -= (new_delta_x - delta_x) / 2.0; + st->draw_maximum_x += (new_delta_x - delta_x) / 2.0; + } + else + { + double new_delta_y + = (delta_x * ((double)st->height)) / ((double)st->width); + st->draw_minimum_y -= (new_delta_y - delta_y) / 2.0; + st->draw_maximum_y += (new_delta_y - delta_y) / 2.0; + } + XCopyArea (st->dpy, st->pixmap, st->window, st->context, 0, 0, st->width, st->height, + 0, 0); + } + st->draw_index++; + /* #### mi->recursion_depth = st->draw_index; */ + + if (st->draw_index >= st->draw_iterations) + { + st->draw_index = 0; + free((void*)st->draw_segments); + st->draw_segments = 0; + return (int) (1000000 * st->delay); + } + else + return (int) (1000000 * st->delay2); +} + +static void +ccurve_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ + struct state *st = (struct state *) closure; + XWindowAttributes xgwa; + st->width = w; + st->height = h; + XGetWindowAttributes (st->dpy, st->window, &xgwa); + XFreePixmap (dpy, st->pixmap); + st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height, + xgwa.depth); +} + +static Bool +ccurve_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + struct state *st = (struct state *) closure; + if (screenhack_event_helper (dpy, window, event)) + { + st->draw_index = 0; + return True; + } + return False; +} + +static void +ccurve_free (Display *dpy, Window window, void *closure) +{ +} + + +static const char *ccurve_defaults [] = +{ + ".background: black", + ".foreground: white", + ".delay: 3", + ".pause: 0.4", + ".limit: 200000", +#ifdef HAVE_MOBILE + "*ignoreRotation: True", +#endif + 0 +}; + +static XrmOptionDescRec ccurve_options [] = +{ + { "-delay", ".delay", XrmoptionSepArg, 0 }, + { "-pause", ".pause", XrmoptionSepArg, 0 }, + { "-limit", ".limit", XrmoptionSepArg, 0 }, + { 0, 0, 0, 0 } +}; + +XSCREENSAVER_MODULE ("CCurve", ccurve) |