1 /* ccurve, Copyright (c) 1998, 1999
2 * Rick Campbell <rick@campbellcentral.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
14 /* Draw self-similar linear fractals including the classic ``C Curve''
16 * 16 Aug 1999 Rick Campbell <rick@campbellcentral.org>
17 * Eliminated sub-windows-with-backing-store-double-buffering crap in
18 * favor of drawing the new image in a pixmap and then splatting that on
21 * 19 Dec 1998 Rick Campbell <rick@campbellcentral.org>
31 #include "screenhack.h"
35 #define SQRT3 (1.73205080756887729353)
36 #define MAXIMUM_COLOR_COUNT (256)
37 #define EPSILON (1e-5)
39 typedef struct Position_struct
46 typedef struct Segment_struct
60 XColor colors [MAXIMUM_COLOR_COUNT];
63 double plot_maximum_x;
64 double plot_maximum_y;
65 double plot_minimum_x;
66 double plot_minimum_y;
69 unsigned long int background;
80 double draw_maximum_x;
81 double draw_maximum_y;
82 double draw_minimum_x;
83 double draw_minimum_y;
84 int draw_segment_count;
85 Segment* draw_segments;
95 /* normalize alters the sequence to go from (0,0) to (1,0) */
97 normalized_plot (int segment_count,
109 for (index = 0; index < segment_count; ++index)
111 Segment* segment = segments + index;
112 double length = segment->length;
113 double angle = segment->angle;
115 x += length * cos (angle);
116 y += length * sin (angle);
117 points [index].x = x;
118 points [index].y = y;
120 angle = -(atan2 (y, x));
121 cosine = cos (angle);
123 length = sqrt ((x * x) + (y * y));
124 /* rotate and scale */
125 for (index = 0; index < segment_count; ++index)
127 double temp_x = points [index].x;
128 double temp_y = points [index].y;
129 points [index].x = ((temp_x * cosine) + (temp_y * (-sine))) / length;
130 points [index].y = ((temp_x * sine) + (temp_y * cosine)) / length;
135 copy_points (int segment_count,
141 for (index = 0; index < segment_count; ++index)
143 target [index] = source [index];
157 double delta_x = 0.0;
158 double delta_y = 0.0;
165 angle = atan2 (delta_y, delta_x);
166 cosine = cos (angle);
168 length = sqrt ((delta_x * delta_x) + (delta_y * delta_y));
169 /* rotate, scale, then shift */
170 for (index = 0; index < segment_count; ++index)
172 double temp_x = points [index].x;
173 double temp_y = points [index].y;
175 = (length * ((temp_x * cosine) + (temp_y * (-sine)))) + x1;
177 = (length * ((temp_x * sine) + (temp_y * cosine))) + y1;
182 self_similar_normalized (struct state *st,
197 double delta_x = maximum_x - minimum_x;
198 double delta_y = maximum_y - minimum_y;
199 st->color_index = (int)(((double)(st->line_count * st->color_count))
200 / ((double)st->total_lines));
202 XSetForeground (st->dpy, st->context, st->colors [st->color_index].pixel);
203 if (st->plot_maximum_x < x1) st->plot_maximum_x = x1;
204 if (st->plot_maximum_x < x2) st->plot_maximum_x = x2;
205 if (st->plot_maximum_y < y1) st->plot_maximum_y = y1;
206 if (st->plot_maximum_y < y2) st->plot_maximum_y = y2;
207 if (st->plot_minimum_x > x1) st->plot_minimum_x = x1;
208 if (st->plot_minimum_x > x2) st->plot_minimum_x = x2;
209 if (st->plot_minimum_y > y1) st->plot_minimum_y = y1;
210 if (st->plot_minimum_y > y2) st->plot_minimum_y = y2;
211 XDrawLine (st->dpy, st->pixmap, st->context,
212 (int)(((x1 - minimum_x) / delta_x) * st->width),
213 (int)(((maximum_y - y1) / delta_y) * st->height),
214 (int)(((x2 - minimum_x) / delta_x) * st->width),
215 (int)(((maximum_y - y2) / delta_y) * st->height));
222 Position* replacement = (Position*)NULL;
226 replacement = (Position*)(malloc (segment_count * sizeof (Segment)));
227 copy_points (segment_count, points, replacement);
228 assert (fabs ((replacement [segment_count - 1].x) - 1.0) < EPSILON);
229 assert (fabs (replacement [segment_count - 1].y) < EPSILON);
230 realign (x1, y1, x2, y2, segment_count, replacement);
231 /* jwz: I don't understand what these assertions are supposed to
232 be detecting, but let's just bail on the fractal instead of
234 /* assert (fabs (x2 - (replacement [segment_count - 1].x)) < EPSILON);
235 assert (fabs (y2 - (replacement [segment_count - 1].y)) < EPSILON);*/
236 if (fabs (x2 - (replacement [segment_count - 1].x)) >= EPSILON ||
237 fabs (y2 - (replacement [segment_count - 1].y)) >= EPSILON) {
243 for (index = 0; index < segment_count; ++index)
245 next_x = replacement [index].x;
246 next_y = replacement [index].y;
247 if (!self_similar_normalized (st,
248 iterations - 1, x, y, next_x, next_y,
249 maximum_x, maximum_y,
250 minimum_x, minimum_y,
251 segment_count, points)) {
264 self_similar (struct state *st,
281 Position* points = (Position*)NULL;
283 points = (Position*)(malloc (segment_count * sizeof (Position)));
284 normalized_plot (segment_count, segments, points);
285 assert (fabs ((points [segment_count - 1].x) - 1.0) < EPSILON);
286 assert (fabs (points [segment_count - 1].y) < EPSILON);
287 self_similar_normalized (st, iterations,
289 maximum_x, maximum_y,
290 minimum_x, minimum_y,
291 segment_count, points);
297 random_double (double base,
302 unsigned int steps = 0;
304 assert (base < limit);
305 assert (epsilon > 0.0);
306 range = limit - base;
307 steps = (unsigned int)(floor (range / epsilon));
308 return base + ((random () % steps) * epsilon);
312 select_2_pattern (Segment* segments)
314 if ((random () % 2) == 0)
316 if ((random () % 2) == 0)
318 segments [0].angle = -M_PI_4;
319 segments [0].length = M_SQRT2;
320 segments [1].angle = M_PI_4;
321 segments [1].length = M_SQRT2;
325 segments [0].angle = M_PI_4;
326 segments [0].length = M_SQRT2;
327 segments [1].angle = -M_PI_4;
328 segments [1].length = M_SQRT2;
334 = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
335 segments [0].length = random_double (0.25, 0.67, 0.001);
336 if ((random () % 2) == 0)
338 segments [1].angle = -(segments [0].angle);
339 segments [1].length = segments [0].length;
343 segments [1].angle = random_double ((-M_PI) / 3.0,
346 segments [1].length = random_double (0.25, 0.67, 0.001);
352 select_3_pattern (Segment* segments)
354 switch (random () % 5)
357 if ((random () % 2) == 0)
359 segments [0].angle = M_PI_4;
360 segments [0].length = M_SQRT2 / 4.0;
361 segments [1].angle = -M_PI_4;
362 segments [1].length = M_SQRT2 / 2.0;
363 segments [2].angle = M_PI_4;
364 segments [2].length = M_SQRT2 / 4.0;
368 segments [0].angle = -M_PI_4;
369 segments [0].length = M_SQRT2 / 4.0;
370 segments [1].angle = M_PI_4;
371 segments [1].length = M_SQRT2 / 2.0;
372 segments [2].angle = -M_PI_4;
373 segments [2].length = M_SQRT2 / 4.0;
377 if ((random () % 2) == 0)
379 segments [0].angle = M_PI / 6.0;
380 segments [0].length = 1.0;
381 segments [1].angle = -M_PI_2;
382 segments [1].length = 1.0;
383 segments [2].angle = M_PI / 6.0;
384 segments [2].length = 1.0;
388 segments [0].angle = -M_PI / 6.0;
389 segments [0].length = 1.0;
390 segments [1].angle = M_PI_2;
391 segments [1].length = 1.0;
392 segments [2].angle = -M_PI / 6.0;
393 segments [2].length = 1.0;
400 = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
401 segments [0].length = random_double (0.25, 0.67, 0.001);
403 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
404 segments [1].length = random_double (0.25, 0.67, 0.001);
405 if ((random () % 3) == 0)
407 if ((random () % 2) == 0)
409 segments [2].angle = segments [0].angle;
413 segments [2].angle = -(segments [0].angle);
415 segments [2].length = segments [0].length;
420 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
421 segments [2].length = random_double (0.25, 0.67, 0.001);
428 select_4_pattern (Segment* segments)
430 switch (random () % 9)
433 if ((random () % 2) == 0)
435 double length = random_double (0.25, 0.50, 0.001);
437 segments [0].angle = 0.0;
438 segments [0].length = 0.5;
439 segments [1].angle = M_PI_2;
440 segments [1].length = length;
441 segments [2].angle = -M_PI_2;
442 segments [2].length = length;
443 segments [3].angle = 0.0;
444 segments [3].length = 0.5;
448 double length = random_double (0.25, 0.50, 0.001);
450 segments [0].angle = 0.0;
451 segments [0].length = 0.5;
452 segments [1].angle = -M_PI_2;
453 segments [1].length = length;
454 segments [2].angle = M_PI_2;
455 segments [2].length = length;
456 segments [3].angle = 0.0;
457 segments [3].length = 0.5;
461 if ((random () % 2) == 0)
463 segments [0].angle = 0.0;
464 segments [0].length = 0.5;
465 segments [1].angle = M_PI_2;
466 segments [1].length = 0.45;
467 segments [2].angle = -M_PI_2;
468 segments [2].length = 0.45;
469 segments [3].angle = 0.0;
470 segments [3].length = 0.5;
474 segments [0].angle = 0.0;
475 segments [0].length = 0.5;
476 segments [1].angle = -M_PI_2;
477 segments [1].length = 0.45;
478 segments [2].angle = M_PI_2;
479 segments [2].length = 0.45;
480 segments [3].angle = 0.0;
481 segments [3].length = 0.5;
485 if ((random () % 2) == 0)
487 segments [0].angle = 0.0;
488 segments [0].length = 1.0;
489 segments [1].angle = (5.0 * M_PI) / 12.0;
490 segments [1].length = 1.2;
491 segments [2].angle = (-5.0 * M_PI) / 12.0;
492 segments [2].length = 1.2;
493 segments [3].angle = 0.0;
494 segments [3].length = 1.0;
498 segments [0].angle = 0.0;
499 segments [0].length = 1.0;
500 segments [1].angle = (-5.0 * M_PI) / 12.0;
501 segments [1].length = 1.2;
502 segments [2].angle = (5.0 * M_PI) / 12.0;
503 segments [2].length = 1.2;
504 segments [3].angle = 0.0;
505 segments [3].length = 1.0;
509 if ((random () % 2) == 0)
512 = random_double (M_PI / 4.0,
516 segments [0].angle = 0.0;
517 segments [0].length = 1.0;
518 segments [1].angle = angle;
519 segments [1].length = 1.2;
520 segments [2].angle = (-angle);
521 segments [2].length = 1.2;
522 segments [3].angle = 0.0;
523 segments [3].length = 1.0;
528 = random_double (M_PI / 4.0,
532 segments [0].angle = 0.0;
533 segments [0].length = 1.0;
534 segments [1].angle = (-angle);
535 segments [1].length = 1.2;
536 segments [2].angle = angle;
537 segments [2].length = 1.2;
538 segments [3].angle = 0.0;
539 segments [3].length = 1.0;
543 if ((random () % 2) == 0)
546 = random_double (M_PI / 4.0,
550 segments [0].angle = 0.0;
551 segments [0].length = 1.0;
552 segments [1].angle = angle;
553 segments [1].length = 1.2;
554 segments [2].angle = (-angle);
555 segments [2].length = 1.2;
556 segments [3].angle = 0.0;
557 segments [3].length = 1.0;
562 = random_double (M_PI / 4.0,
566 segments [0].angle = 0.0;
567 segments [0].length = 1.0;
568 segments [1].angle = (-angle);
569 segments [1].length = 1.2;
570 segments [2].angle = angle;
571 segments [2].length = 1.2;
572 segments [3].angle = 0.0;
573 segments [3].length = 1.0;
577 if ((random () % 2) == 0)
580 = random_double (M_PI / 4.0,
583 double length = random_double (0.25, 0.50, 0.001);
585 segments [0].angle = 0.0;
586 segments [0].length = 1.0;
587 segments [1].angle = angle;
588 segments [1].length = length;
589 segments [2].angle = (-angle);
590 segments [2].length = length;
591 segments [3].angle = 0.0;
592 segments [3].length = 1.0;
597 = random_double (M_PI / 4.0,
600 double length = random_double (0.25, 0.50, 0.001);
602 segments [0].angle = 0.0;
603 segments [0].length = 1.0;
604 segments [1].angle = (-angle);
605 segments [1].length = length;
606 segments [2].angle = angle;
607 segments [2].length = length;
608 segments [3].angle = 0.0;
609 segments [3].length = 1.0;
616 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
617 segments [0].length = random_double (0.25, 0.50, 0.001);
619 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
620 segments [1].length = random_double (0.25, 0.50, 0.001);
621 if ((random () % 3) == 0)
624 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
625 segments [2].length = random_double (0.25, 0.50, 0.001);
627 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
628 segments [3].length = random_double (0.25, 0.50, 0.001);
632 if ((random () % 2) == 0)
634 segments [2].angle = -(segments [1].angle);
635 segments [2].length = segments [1].length;
636 segments [3].angle = -(segments [0].angle);
637 segments [3].length = segments [0].length;
641 segments [2].angle = segments [1].angle;
642 segments [2].length = segments [1].length;
643 segments [3].angle = segments [0].angle;
644 segments [3].length = segments [0].length;
652 select_pattern (int segment_count,
655 switch (segment_count)
658 select_2_pattern (segments);
661 select_3_pattern (segments);
664 select_4_pattern (segments);
667 fprintf (stderr, "\nBad segment count, must be 2, 3, or 4.\n");
672 #define Y_START (0.5)
675 ccurve_init (Display *dpy, Window window)
677 struct state *st = (struct state *) calloc (1, sizeof(*st));
678 unsigned long int black = 0;
680 XWindowAttributes hack_attributes;
682 unsigned long int white = 0;
687 st->delay = get_float_resource (st->dpy, "delay", "Integer");
688 st->delay2 = get_float_resource (st->dpy, "pause", "Integer");
689 st->maximum_lines = get_integer_resource (st->dpy, "limit", "Integer");
690 black = BlackPixel (st->dpy, DefaultScreen (st->dpy));
691 white = WhitePixel (st->dpy, DefaultScreen (st->dpy));
692 st->background = black;
693 XGetWindowAttributes (st->dpy, st->window, &hack_attributes);
694 st->width = hack_attributes.width;
695 st->height = hack_attributes.height;
696 depth = hack_attributes.depth;
697 st->color_map = hack_attributes.colormap;
698 st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height, depth);
699 values.foreground = white;
700 values.background = black;
701 st->context = XCreateGC (st->dpy, st->window, GCForeground | GCBackground,
703 st->color_count = MAXIMUM_COLOR_COUNT;
704 make_color_loop (st->dpy, st->color_map,
708 st->colors, &st->color_count, True, False);
709 if (st->color_count <= 0)
712 st->colors [0].red = st->colors [0].green = st->colors [0].blue = 0xFFFF;
713 XAllocColor (st->dpy, st->color_map, &st->colors [0]);
716 st->draw_maximum_x = 1.20;
717 st->draw_maximum_y = 0.525;
718 st->draw_minimum_x = -0.20;
719 st->draw_minimum_y = -0.525;
726 ccurve_draw (Display *dpy, Window window, void *closure)
728 struct state *st = (struct state *) closure;
729 static const int lengths [] = { 4, 4, 4, 4, 4, 3, 3, 3, 2 };
731 if (st->draw_index == 0)
733 st->draw_segment_count
734 = lengths [random () % (sizeof (lengths) / sizeof (int))];
736 = (Segment*)(malloc ((st->draw_segment_count) * sizeof (Segment)));
737 select_pattern (st->draw_segment_count, st->draw_segments);
738 st->draw_iterations = floor (log (st->maximum_lines)
739 / log (((double)(st->draw_segment_count))));
740 if ((random () % 3) != 0)
742 double factor = 0.45;
743 st->draw_x1 += random_double (-factor, factor, 0.001);
744 st->draw_y1 += random_double (-factor, factor, 0.001);
745 st->draw_x2 += random_double (-factor, factor, 0.001);
746 st->draw_y2 += random_double (-factor, factor, 0.001);
748 /* background = (random () % 2) ? black : white; */
752 /* for (st->draw_index = 0; st->draw_index < st->draw_iterations; ++st->draw_index) */
754 double delta_x = 0.0;
755 double delta_y = 0.0;
757 XSetForeground (st->dpy, st->context, st->background);
758 XFillRectangle (st->dpy, st->pixmap, st->context, 0, 0, st->width, st->height);
760 st->total_lines = (int)(pow ((double)(st->draw_segment_count),
761 (double)st->draw_index));
762 st->plot_maximum_x = -1000.00;
763 st->plot_maximum_y = -1000.00;
764 st->plot_minimum_x = 1000.00;
765 st->plot_minimum_y = 1000.00;
766 self_similar (st, st->pixmap, st->context, st->width, st->height, st->draw_index,
767 st->draw_x1, st->draw_y1, st->draw_x2, st->draw_y2,
772 st->draw_segment_count, st->draw_segments);
773 delta_x = st->plot_maximum_x - st->plot_minimum_x;
774 delta_y = st->plot_maximum_y - st->plot_minimum_y;
775 st->draw_maximum_x = st->plot_maximum_x + (delta_x * 0.2);
776 st->draw_maximum_y = st->plot_maximum_y + (delta_y * 0.2);
777 st->draw_minimum_x = st->plot_minimum_x - (delta_x * 0.2);
778 st->draw_minimum_y = st->plot_minimum_y - (delta_y * 0.2);
779 delta_x = st->draw_maximum_x - st->draw_minimum_x;
780 delta_y = st->draw_maximum_y - st->draw_minimum_y;
781 if ((delta_y / delta_x) > (((double)st->height) / ((double)st->width)))
784 = (delta_y * ((double)st->width)) / ((double)st->height);
785 st->draw_minimum_x -= (new_delta_x - delta_x) / 2.0;
786 st->draw_maximum_x += (new_delta_x - delta_x) / 2.0;
791 = (delta_x * ((double)st->height)) / ((double)st->width);
792 st->draw_minimum_y -= (new_delta_y - delta_y) / 2.0;
793 st->draw_maximum_y += (new_delta_y - delta_y) / 2.0;
795 XCopyArea (st->dpy, st->pixmap, st->window, st->context, 0, 0, st->width, st->height,
799 /* #### mi->recursion_depth = st->draw_index; */
801 if (st->draw_index >= st->draw_iterations)
804 free((void*)st->draw_segments);
805 st->draw_segments = 0;
806 return (int) (1000000 * st->delay);
809 return (int) (1000000 * st->delay2);
813 ccurve_reshape (Display *dpy, Window window, void *closure,
814 unsigned int w, unsigned int h)
816 struct state *st = (struct state *) closure;
817 XWindowAttributes xgwa;
820 XGetWindowAttributes (st->dpy, st->window, &xgwa);
821 XFreePixmap (dpy, st->pixmap);
822 st->pixmap = XCreatePixmap (st->dpy, st->window, st->width, st->height,
827 ccurve_event (Display *dpy, Window window, void *closure, XEvent *event)
833 ccurve_free (Display *dpy, Window window, void *closure)
838 static const char *ccurve_defaults [] =
840 ".background: black",
841 ".foreground: white",
848 static XrmOptionDescRec ccurve_options [] =
850 { "-delay", ".delay", XrmoptionSepArg, 0 },
851 { "-pause", ".pause", XrmoptionSepArg, 0 },
852 { "-limit", ".limit", XrmoptionSepArg, 0 },
856 XSCREENSAVER_MODULE ("CCurve", ccurve)