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
53 static int color_count = 0;
54 static int color_index = 0;
55 static Colormap color_map = (Colormap)NULL;
56 static XColor colors [MAXIMUM_COLOR_COUNT];
57 static int line_count = 0;
58 static int maximum_lines = 0;
59 static double plot_maximum_x = -1000.00;
60 static double plot_maximum_y = -1000.00;
61 static double plot_minimum_x = 1000.00;
62 static double plot_minimum_y = 1000.00;
63 static int total_lines = 0;
65 /* normalize alters the sequence to go from (0,0) to (1,0) */
68 normalized_plot (int segment_count,
80 for (index = 0; index < segment_count; ++index)
82 Segment* segment = segments + index;
83 double length = segment->length;
84 double angle = segment->angle;
86 x += length * cos (angle);
87 y += length * sin (angle);
91 angle = -(atan2 (y, x));
94 length = sqrt ((x * x) + (y * y));
95 /* rotate and scale */
96 for (index = 0; index < segment_count; ++index)
98 double temp_x = points [index].x;
99 double temp_y = points [index].y;
100 points [index].x = ((temp_x * cosine) + (temp_y * (-sine))) / length;
101 points [index].y = ((temp_x * sine) + (temp_y * cosine)) / length;
107 copy_points (int segment_count,
113 for (index = 0; index < segment_count; ++index)
115 target [index] = source [index];
130 double delta_x = 0.0;
131 double delta_y = 0.0;
138 angle = atan2 (delta_y, delta_x);
139 cosine = cos (angle);
141 length = sqrt ((delta_x * delta_x) + (delta_y * delta_y));
142 /* rotate, scale, then shift */
143 for (index = 0; index < segment_count; ++index)
145 double temp_x = points [index].x;
146 double temp_y = points [index].y;
148 = (length * ((temp_x * cosine) + (temp_y * (-sine)))) + x1;
150 = (length * ((temp_x * sine) + (temp_y * cosine))) + y1;
156 self_similar_normalized (Display* display,
175 double delta_x = maximum_x - minimum_x;
176 double delta_y = maximum_y - minimum_y;
177 color_index = (int)(((double)(line_count * color_count))
178 / ((double)total_lines));
180 XSetForeground (display, context, colors [color_index].pixel);
181 if (plot_maximum_x < x1) plot_maximum_x = x1;
182 if (plot_maximum_x < x2) plot_maximum_x = x2;
183 if (plot_maximum_y < y1) plot_maximum_y = y1;
184 if (plot_maximum_y < y2) plot_maximum_y = y2;
185 if (plot_minimum_x > x1) plot_minimum_x = x1;
186 if (plot_minimum_x > x2) plot_minimum_x = x2;
187 if (plot_minimum_y > y1) plot_minimum_y = y1;
188 if (plot_minimum_y > y2) plot_minimum_y = y2;
189 XDrawLine (display, pixmap, context,
190 (int)(((x1 - minimum_x) / delta_x) * width),
191 (int)(((maximum_y - y1) / delta_y) * height),
192 (int)(((x2 - minimum_x) / delta_x) * width),
193 (int)(((maximum_y - y2) / delta_y) * height));
200 Position* replacement = (Position*)NULL;
204 replacement = (Position*)(malloc (segment_count * sizeof (Segment)));
205 copy_points (segment_count, points, replacement);
206 assert (fabs ((replacement [segment_count - 1].x) - 1.0) < EPSILON);
207 assert (fabs (replacement [segment_count - 1].y) < EPSILON);
208 realign (x1, y1, x2, y2, segment_count, replacement);
209 assert (fabs (x2 - (replacement [segment_count - 1].x)) < EPSILON);
210 assert (fabs (y2 - (replacement [segment_count - 1].y)) < EPSILON);
213 for (index = 0; index < segment_count; ++index)
215 next_x = replacement [index].x;
216 next_y = replacement [index].y;
217 self_similar_normalized (display, pixmap, context, width, height,
218 iterations - 1, x, y, next_x, next_y,
219 maximum_x, maximum_y,
220 minimum_x, minimum_y,
221 segment_count, points);
225 free((void*)replacement);
231 self_similar (Display* display,
248 Position* points = (Position*)NULL;
250 points = (Position*)(malloc (segment_count * sizeof (Position)));
251 normalized_plot (segment_count, segments, points);
252 assert (fabs ((points [segment_count - 1].x) - 1.0) < EPSILON);
253 assert (fabs (points [segment_count - 1].y) < EPSILON);
254 self_similar_normalized (display, pixmap, context,
255 width, height, iterations,
257 maximum_x, maximum_y,
258 minimum_x, minimum_y,
259 segment_count, points);
265 random_double (double base,
270 unsigned int steps = 0;
272 assert (base < limit);
273 assert (epsilon > 0.0);
274 range = limit - base;
275 steps = (unsigned int)(floor (range / epsilon));
276 return base + ((random () % steps) * epsilon);
281 select_2_pattern (Segment* segments)
283 if ((random () % 2) == 0)
285 if ((random () % 2) == 0)
287 segments [0].angle = -M_PI_4;
288 segments [0].length = M_SQRT2;
289 segments [1].angle = M_PI_4;
290 segments [1].length = M_SQRT2;
294 segments [0].angle = M_PI_4;
295 segments [0].length = M_SQRT2;
296 segments [1].angle = -M_PI_4;
297 segments [1].length = M_SQRT2;
303 = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
304 segments [0].length = random_double (0.25, 0.67, 0.001);
305 if ((random () % 2) == 0)
307 segments [1].angle = -(segments [0].angle);
308 segments [1].length = segments [0].length;
312 segments [1].angle = random_double ((-M_PI) / 3.0,
315 segments [1].length = random_double (0.25, 0.67, 0.001);
322 select_3_pattern (Segment* segments)
324 switch (random () % 5)
327 if ((random () % 2) == 0)
329 segments [0].angle = M_PI_4;
330 segments [0].length = M_SQRT2 / 4.0;
331 segments [1].angle = -M_PI_4;
332 segments [1].length = M_SQRT2 / 2.0;
333 segments [2].angle = M_PI_4;
334 segments [2].length = M_SQRT2 / 4.0;
338 segments [0].angle = -M_PI_4;
339 segments [0].length = M_SQRT2 / 4.0;
340 segments [1].angle = M_PI_4;
341 segments [1].length = M_SQRT2 / 2.0;
342 segments [2].angle = -M_PI_4;
343 segments [2].length = M_SQRT2 / 4.0;
347 if ((random () % 2) == 0)
349 segments [0].angle = M_PI / 6.0;
350 segments [0].length = 1.0;
351 segments [1].angle = -M_PI_2;
352 segments [1].length = 1.0;
353 segments [2].angle = M_PI / 6.0;
354 segments [2].length = 1.0;
358 segments [0].angle = -M_PI / 6.0;
359 segments [0].length = 1.0;
360 segments [1].angle = M_PI_2;
361 segments [1].length = 1.0;
362 segments [2].angle = -M_PI / 6.0;
363 segments [2].length = 1.0;
370 = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
371 segments [0].length = random_double (0.25, 0.67, 0.001);
373 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
374 segments [1].length = random_double (0.25, 0.67, 0.001);
375 if ((random () % 3) == 0)
377 if ((random () % 2) == 0)
379 segments [2].angle = segments [0].angle;
383 segments [2].angle = -(segments [0].angle);
385 segments [2].length = segments [0].length;
390 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
391 segments [2].length = random_double (0.25, 0.67, 0.001);
399 select_4_pattern (Segment* segments)
401 switch (random () % 9)
404 if ((random () % 2) == 0)
406 double length = random_double (0.25, 0.50, 0.001);
408 segments [0].angle = 0.0;
409 segments [0].length = 0.5;
410 segments [1].angle = M_PI_2;
411 segments [1].length = length;
412 segments [2].angle = -M_PI_2;
413 segments [2].length = length;
414 segments [3].angle = 0.0;
415 segments [3].length = 0.5;
419 double length = random_double (0.25, 0.50, 0.001);
421 segments [0].angle = 0.0;
422 segments [0].length = 0.5;
423 segments [1].angle = -M_PI_2;
424 segments [1].length = length;
425 segments [2].angle = M_PI_2;
426 segments [2].length = length;
427 segments [3].angle = 0.0;
428 segments [3].length = 0.5;
432 if ((random () % 2) == 0)
434 segments [0].angle = 0.0;
435 segments [0].length = 0.5;
436 segments [1].angle = M_PI_2;
437 segments [1].length = 0.45;
438 segments [2].angle = -M_PI_2;
439 segments [2].length = 0.45;
440 segments [3].angle = 0.0;
441 segments [3].length = 0.5;
445 segments [0].angle = 0.0;
446 segments [0].length = 0.5;
447 segments [1].angle = -M_PI_2;
448 segments [1].length = 0.45;
449 segments [2].angle = M_PI_2;
450 segments [2].length = 0.45;
451 segments [3].angle = 0.0;
452 segments [3].length = 0.5;
456 if ((random () % 2) == 0)
458 segments [0].angle = 0.0;
459 segments [0].length = 1.0;
460 segments [1].angle = (5.0 * M_PI) / 12.0;
461 segments [1].length = 1.2;
462 segments [2].angle = (-5.0 * M_PI) / 12.0;
463 segments [2].length = 1.2;
464 segments [3].angle = 0.0;
465 segments [3].length = 1.0;
469 segments [0].angle = 0.0;
470 segments [0].length = 1.0;
471 segments [1].angle = (-5.0 * M_PI) / 12.0;
472 segments [1].length = 1.2;
473 segments [2].angle = (5.0 * M_PI) / 12.0;
474 segments [2].length = 1.2;
475 segments [3].angle = 0.0;
476 segments [3].length = 1.0;
480 if ((random () % 2) == 0)
483 = random_double (M_PI / 4.0,
487 segments [0].angle = 0.0;
488 segments [0].length = 1.0;
489 segments [1].angle = angle;
490 segments [1].length = 1.2;
491 segments [2].angle = (-angle);
492 segments [2].length = 1.2;
493 segments [3].angle = 0.0;
494 segments [3].length = 1.0;
499 = random_double (M_PI / 4.0,
503 segments [0].angle = 0.0;
504 segments [0].length = 1.0;
505 segments [1].angle = (-angle);
506 segments [1].length = 1.2;
507 segments [2].angle = angle;
508 segments [2].length = 1.2;
509 segments [3].angle = 0.0;
510 segments [3].length = 1.0;
514 if ((random () % 2) == 0)
517 = random_double (M_PI / 4.0,
521 segments [0].angle = 0.0;
522 segments [0].length = 1.0;
523 segments [1].angle = angle;
524 segments [1].length = 1.2;
525 segments [2].angle = (-angle);
526 segments [2].length = 1.2;
527 segments [3].angle = 0.0;
528 segments [3].length = 1.0;
533 = random_double (M_PI / 4.0,
537 segments [0].angle = 0.0;
538 segments [0].length = 1.0;
539 segments [1].angle = (-angle);
540 segments [1].length = 1.2;
541 segments [2].angle = angle;
542 segments [2].length = 1.2;
543 segments [3].angle = 0.0;
544 segments [3].length = 1.0;
548 if ((random () % 2) == 0)
551 = random_double (M_PI / 4.0,
554 double length = random_double (0.25, 0.50, 0.001);
556 segments [0].angle = 0.0;
557 segments [0].length = 1.0;
558 segments [1].angle = angle;
559 segments [1].length = length;
560 segments [2].angle = (-angle);
561 segments [2].length = length;
562 segments [3].angle = 0.0;
563 segments [3].length = 1.0;
568 = random_double (M_PI / 4.0,
571 double length = random_double (0.25, 0.50, 0.001);
573 segments [0].angle = 0.0;
574 segments [0].length = 1.0;
575 segments [1].angle = (-angle);
576 segments [1].length = length;
577 segments [2].angle = angle;
578 segments [2].length = length;
579 segments [3].angle = 0.0;
580 segments [3].length = 1.0;
587 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
588 segments [0].length = random_double (0.25, 0.50, 0.001);
590 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
591 segments [1].length = random_double (0.25, 0.50, 0.001);
592 if ((random () % 3) == 0)
595 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
596 segments [2].length = random_double (0.25, 0.50, 0.001);
598 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
599 segments [3].length = random_double (0.25, 0.50, 0.001);
603 if ((random () % 2) == 0)
605 segments [2].angle = -(segments [1].angle);
606 segments [2].length = segments [1].length;
607 segments [3].angle = -(segments [0].angle);
608 segments [3].length = segments [0].length;
612 segments [2].angle = segments [1].angle;
613 segments [2].length = segments [1].length;
614 segments [3].angle = segments [0].angle;
615 segments [3].length = segments [0].length;
623 select_pattern (int segment_count,
626 switch (segment_count)
629 select_2_pattern (segments);
632 select_3_pattern (segments);
635 select_4_pattern (segments);
638 fprintf (stderr, "\nBad segment count, must be 2, 3, or 4.\n");
643 char *progclass = "Ccurve";
653 XrmOptionDescRec options [] =
655 { "-delay", ".delay", XrmoptionSepArg, 0 },
656 { "-pause", ".pause", XrmoptionSepArg, 0 },
657 { "-limit", ".limit", XrmoptionSepArg, 0 },
661 #define Y_START (0.5)
664 screenhack (Display* display,
667 unsigned long int background = 0;
668 unsigned long int black = 0;
672 Pixmap pixmap = (Pixmap)NULL;
673 XWindowAttributes hack_attributes;
678 unsigned long int white = 0;
681 delay = get_integer_resource ("delay", "Integer");
682 pause = get_integer_resource ("pause", "Integer");
683 maximum_lines = get_integer_resource ("limit", "Integer");
684 black = BlackPixel (display, DefaultScreen (display));
685 white = WhitePixel (display, DefaultScreen (display));
687 XGetWindowAttributes (display, window, &hack_attributes);
688 width = hack_attributes.width;
689 height = hack_attributes.height;
690 depth = hack_attributes.depth;
691 color_map = hack_attributes.colormap;
692 pixmap = XCreatePixmap (display, window, width, height, depth);
693 values.foreground = white;
694 values.background = black;
695 context = XCreateGC (display, window, GCForeground | GCBackground,
697 color_count = MAXIMUM_COLOR_COUNT;
698 make_color_loop (display, color_map,
702 colors, &color_count, True, False);
703 if (color_count <= 0)
706 colors [0].red = colors [0].green = colors [0].blue = 0xFFFF;
707 XAllocColor (display, color_map, &colors [0]);
713 double maximum_x = 1.20;
714 double maximum_y = 0.525;
715 double minimum_x = -0.20;
716 double minimum_y = -0.525;
717 static int lengths [] = { 4, 4, 4, 4, 4, 3, 3, 3, 2 };
718 int segment_count = 0;
719 Segment* segments = (Segment*)NULL;
726 = lengths [random () % (sizeof (lengths) / sizeof (int))];
728 = (Segment*)(malloc ((segment_count) * sizeof (Segment)));
729 select_pattern (segment_count, segments);
730 iterations = floor (log (maximum_lines)
731 / log (((double)(segment_count))));
732 if ((random () % 3) != 0)
734 double factor = 0.45;
735 x1 += random_double (-factor, factor, 0.001);
736 y1 += random_double (-factor, factor, 0.001);
737 x2 += random_double (-factor, factor, 0.001);
738 y2 += random_double (-factor, factor, 0.001);
740 /* background = (random () % 2) ? black : white; */
741 for (index = 0; index < iterations; ++index)
743 double delta_x = 0.0;
744 double delta_y = 0.0;
746 XSetForeground (display, context, background);
747 XFillRectangle (display, pixmap, context, 0, 0, width, height);
749 total_lines = (int)(pow ((double)(segment_count),
751 plot_maximum_x = -1000.00;
752 plot_maximum_y = -1000.00;
753 plot_minimum_x = 1000.00;
754 plot_minimum_y = 1000.00;
755 self_similar (display, pixmap, context, width, height, index,
761 segment_count, segments);
762 delta_x = plot_maximum_x - plot_minimum_x;
763 delta_y = plot_maximum_y - plot_minimum_y;
764 maximum_x = plot_maximum_x + (delta_x * 0.2);
765 maximum_y = plot_maximum_y + (delta_y * 0.2);
766 minimum_x = plot_minimum_x - (delta_x * 0.2);
767 minimum_y = plot_minimum_y - (delta_y * 0.2);
768 delta_x = maximum_x - minimum_x;
769 delta_y = maximum_y - minimum_y;
770 if ((delta_y / delta_x) > (((double)height) / ((double)width)))
773 = (delta_y * ((double)width)) / ((double)height);
774 minimum_x -= (new_delta_x - delta_x) / 2.0;
775 maximum_x += (new_delta_x - delta_x) / 2.0;
780 = (delta_x * ((double)height)) / ((double)width);
781 minimum_y -= (new_delta_y - delta_y) / 2.0;
782 maximum_y += (new_delta_y - delta_y) / 2.0;
784 XCopyArea (display, pixmap, window, context, 0, 0, width, height,
786 if (delay) sleep (delay);
787 screenhack_handle_events (display);
789 free((void*)segments);
790 if (pause) sleep (pause);
791 erase_full_window (display, window);