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*)(alloca (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);
230 self_similar (Display* display,
247 Position* points = (Position*)NULL;
249 points = (Position*)(alloca (segment_count * sizeof (Position)));
250 normalized_plot (segment_count, segments, points);
251 assert (fabs ((points [segment_count - 1].x) - 1.0) < EPSILON);
252 assert (fabs (points [segment_count - 1].y) < EPSILON);
253 self_similar_normalized (display, pixmap, context,
254 width, height, iterations,
256 maximum_x, maximum_y,
257 minimum_x, minimum_y,
258 segment_count, points);
263 random_double (double base,
268 unsigned int steps = 0;
270 assert (base < limit);
271 assert (epsilon > 0.0);
272 range = limit - base;
273 steps = (unsigned int)(floor (range / epsilon));
274 return base + ((random () % steps) * epsilon);
279 select_2_pattern (Segment* segments)
281 if ((random () % 2) == 0)
283 if ((random () % 2) == 0)
285 segments [0].angle = -M_PI_4;
286 segments [0].length = M_SQRT2;
287 segments [1].angle = M_PI_4;
288 segments [1].length = M_SQRT2;
292 segments [0].angle = M_PI_4;
293 segments [0].length = M_SQRT2;
294 segments [1].angle = -M_PI_4;
295 segments [1].length = M_SQRT2;
301 = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
302 segments [0].length = random_double (0.25, 0.67, 0.001);
303 if ((random () % 2) == 0)
305 segments [1].angle = -(segments [0].angle);
306 segments [1].length = segments [0].length;
310 segments [1].angle = random_double ((-M_PI) / 3.0,
313 segments [1].length = random_double (0.25, 0.67, 0.001);
320 select_3_pattern (Segment* segments)
322 switch (random () % 5)
325 if ((random () % 2) == 0)
327 segments [0].angle = M_PI_4;
328 segments [0].length = M_SQRT2 / 4.0;
329 segments [1].angle = -M_PI_4;
330 segments [1].length = M_SQRT2 / 2.0;
331 segments [2].angle = M_PI_4;
332 segments [2].length = M_SQRT2 / 4.0;
336 segments [0].angle = -M_PI_4;
337 segments [0].length = M_SQRT2 / 4.0;
338 segments [1].angle = M_PI_4;
339 segments [1].length = M_SQRT2 / 2.0;
340 segments [2].angle = -M_PI_4;
341 segments [2].length = M_SQRT2 / 4.0;
345 if ((random () % 2) == 0)
347 segments [0].angle = M_PI / 6.0;
348 segments [0].length = 1.0;
349 segments [1].angle = -M_PI_2;
350 segments [1].length = 1.0;
351 segments [2].angle = M_PI / 6.0;
352 segments [2].length = 1.0;
356 segments [0].angle = -M_PI / 6.0;
357 segments [0].length = 1.0;
358 segments [1].angle = M_PI_2;
359 segments [1].length = 1.0;
360 segments [2].angle = -M_PI / 6.0;
361 segments [2].length = 1.0;
368 = random_double (M_PI / 6.0, M_PI / 3.0, M_PI / 180.0);
369 segments [0].length = random_double (0.25, 0.67, 0.001);
371 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
372 segments [1].length = random_double (0.25, 0.67, 0.001);
373 if ((random () % 3) == 0)
375 if ((random () % 2) == 0)
377 segments [2].angle = segments [0].angle;
381 segments [2].angle = -(segments [0].angle);
383 segments [2].length = segments [0].length;
388 = random_double (-M_PI / 3.0, -M_PI / 6.0, M_PI / 180.0);
389 segments [2].length = random_double (0.25, 0.67, 0.001);
397 select_4_pattern (Segment* segments)
399 switch (random () % 9)
402 if ((random () % 2) == 0)
404 double length = random_double (0.25, 0.50, 0.001);
406 segments [0].angle = 0.0;
407 segments [0].length = 0.5;
408 segments [1].angle = M_PI_2;
409 segments [1].length = length;
410 segments [2].angle = -M_PI_2;
411 segments [2].length = length;
412 segments [3].angle = 0.0;
413 segments [3].length = 0.5;
417 double length = random_double (0.25, 0.50, 0.001);
419 segments [0].angle = 0.0;
420 segments [0].length = 0.5;
421 segments [1].angle = -M_PI_2;
422 segments [1].length = length;
423 segments [2].angle = M_PI_2;
424 segments [2].length = length;
425 segments [3].angle = 0.0;
426 segments [3].length = 0.5;
430 if ((random () % 2) == 0)
432 segments [0].angle = 0.0;
433 segments [0].length = 0.5;
434 segments [1].angle = M_PI_2;
435 segments [1].length = 0.45;
436 segments [2].angle = -M_PI_2;
437 segments [2].length = 0.45;
438 segments [3].angle = 0.0;
439 segments [3].length = 0.5;
443 segments [0].angle = 0.0;
444 segments [0].length = 0.5;
445 segments [1].angle = -M_PI_2;
446 segments [1].length = 0.45;
447 segments [2].angle = M_PI_2;
448 segments [2].length = 0.45;
449 segments [3].angle = 0.0;
450 segments [3].length = 0.5;
454 if ((random () % 2) == 0)
456 segments [0].angle = 0.0;
457 segments [0].length = 1.0;
458 segments [1].angle = (5.0 * M_PI) / 12.0;
459 segments [1].length = 1.2;
460 segments [2].angle = (-5.0 * M_PI) / 12.0;
461 segments [2].length = 1.2;
462 segments [3].angle = 0.0;
463 segments [3].length = 1.0;
467 segments [0].angle = 0.0;
468 segments [0].length = 1.0;
469 segments [1].angle = (-5.0 * M_PI) / 12.0;
470 segments [1].length = 1.2;
471 segments [2].angle = (5.0 * M_PI) / 12.0;
472 segments [2].length = 1.2;
473 segments [3].angle = 0.0;
474 segments [3].length = 1.0;
478 if ((random () % 2) == 0)
481 = random_double (M_PI / 4.0,
485 segments [0].angle = 0.0;
486 segments [0].length = 1.0;
487 segments [1].angle = angle;
488 segments [1].length = 1.2;
489 segments [2].angle = (-angle);
490 segments [2].length = 1.2;
491 segments [3].angle = 0.0;
492 segments [3].length = 1.0;
497 = random_double (M_PI / 4.0,
501 segments [0].angle = 0.0;
502 segments [0].length = 1.0;
503 segments [1].angle = (-angle);
504 segments [1].length = 1.2;
505 segments [2].angle = angle;
506 segments [2].length = 1.2;
507 segments [3].angle = 0.0;
508 segments [3].length = 1.0;
512 if ((random () % 2) == 0)
515 = random_double (M_PI / 4.0,
519 segments [0].angle = 0.0;
520 segments [0].length = 1.0;
521 segments [1].angle = angle;
522 segments [1].length = 1.2;
523 segments [2].angle = (-angle);
524 segments [2].length = 1.2;
525 segments [3].angle = 0.0;
526 segments [3].length = 1.0;
531 = random_double (M_PI / 4.0,
535 segments [0].angle = 0.0;
536 segments [0].length = 1.0;
537 segments [1].angle = (-angle);
538 segments [1].length = 1.2;
539 segments [2].angle = angle;
540 segments [2].length = 1.2;
541 segments [3].angle = 0.0;
542 segments [3].length = 1.0;
546 if ((random () % 2) == 0)
549 = random_double (M_PI / 4.0,
552 double length = random_double (0.25, 0.50, 0.001);
554 segments [0].angle = 0.0;
555 segments [0].length = 1.0;
556 segments [1].angle = angle;
557 segments [1].length = length;
558 segments [2].angle = (-angle);
559 segments [2].length = length;
560 segments [3].angle = 0.0;
561 segments [3].length = 1.0;
566 = random_double (M_PI / 4.0,
569 double length = random_double (0.25, 0.50, 0.001);
571 segments [0].angle = 0.0;
572 segments [0].length = 1.0;
573 segments [1].angle = (-angle);
574 segments [1].length = length;
575 segments [2].angle = angle;
576 segments [2].length = length;
577 segments [3].angle = 0.0;
578 segments [3].length = 1.0;
585 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
586 segments [0].length = random_double (0.25, 0.50, 0.001);
588 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
589 segments [1].length = random_double (0.25, 0.50, 0.001);
590 if ((random () % 3) == 0)
593 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
594 segments [2].length = random_double (0.25, 0.50, 0.001);
596 = random_double (M_PI / 12.0, (11.0 * M_PI) / 12.0, 0.001);
597 segments [3].length = random_double (0.25, 0.50, 0.001);
601 if ((random () % 2) == 0)
603 segments [2].angle = -(segments [1].angle);
604 segments [2].length = segments [1].length;
605 segments [3].angle = -(segments [0].angle);
606 segments [3].length = segments [0].length;
610 segments [2].angle = segments [1].angle;
611 segments [2].length = segments [1].length;
612 segments [3].angle = segments [0].angle;
613 segments [3].length = segments [0].length;
621 select_pattern (int segment_count,
624 switch (segment_count)
627 select_2_pattern (segments);
630 select_3_pattern (segments);
633 select_4_pattern (segments);
636 fprintf (stderr, "\nBad segment count, must be 2, 3, or 4.\n");
641 char *progclass = "Ccurve";
651 XrmOptionDescRec options [] =
653 { "-delay", ".delay", XrmoptionSepArg, 0 },
654 { "-pause", ".pause", XrmoptionSepArg, 0 },
655 { "-limit", ".limit", XrmoptionSepArg, 0 },
659 #define Y_START (0.5)
662 screenhack (Display* display,
665 unsigned long int background = 0;
666 unsigned long int black = 0;
670 Pixmap pixmap = (Pixmap)NULL;
671 XWindowAttributes hack_attributes;
676 unsigned long int white = 0;
679 delay = get_integer_resource ("delay", "Integer");
680 pause = get_integer_resource ("pause", "Integer");
681 maximum_lines = get_integer_resource ("limit", "Integer");
682 black = BlackPixel (display, DefaultScreen (display));
683 white = WhitePixel (display, DefaultScreen (display));
685 XGetWindowAttributes (display, window, &hack_attributes);
686 width = hack_attributes.width;
687 height = hack_attributes.height;
688 depth = hack_attributes.depth;
689 color_map = hack_attributes.colormap;
690 pixmap = XCreatePixmap (display, window, width, height, depth);
691 values.foreground = white;
692 values.background = black;
693 context = XCreateGC (display, window, GCForeground | GCBackground,
695 color_count = MAXIMUM_COLOR_COUNT;
696 make_color_loop (display, color_map,
700 colors, &color_count, True, False);
701 if (color_count <= 0)
704 colors [0].red = colors [0].green = colors [0].blue = 0xFFFF;
705 XAllocColor (display, color_map, &colors [0]);
711 double maximum_x = 1.20;
712 double maximum_y = 0.525;
713 double minimum_x = -0.20;
714 double minimum_y = -0.525;
715 static int lengths [] = { 4, 4, 4, 4, 4, 3, 3, 3, 2 };
716 int segment_count = 0;
717 Segment* segments = (Segment*)NULL;
724 = lengths [random () % (sizeof (lengths) / sizeof (int))];
726 = (Segment*)(alloca ((segment_count) * sizeof (Segment)));
727 select_pattern (segment_count, segments);
728 iterations = floor (log (maximum_lines)
729 / log (((double)(segment_count))));
730 if ((random () % 3) != 0)
732 double factor = 0.45;
733 x1 += random_double (-factor, factor, 0.001);
734 y1 += random_double (-factor, factor, 0.001);
735 x2 += random_double (-factor, factor, 0.001);
736 y2 += random_double (-factor, factor, 0.001);
738 /* background = (random () % 2) ? black : white; */
739 for (index = 0; index < iterations; ++index)
741 double delta_x = 0.0;
742 double delta_y = 0.0;
744 XSetForeground (display, context, background);
745 XFillRectangle (display, pixmap, context, 0, 0, width, height);
747 total_lines = (int)(pow ((double)(segment_count),
749 plot_maximum_x = -1000.00;
750 plot_maximum_y = -1000.00;
751 plot_minimum_x = 1000.00;
752 plot_minimum_y = 1000.00;
753 self_similar (display, pixmap, context, width, height, index,
759 segment_count, segments);
760 delta_x = plot_maximum_x - plot_minimum_x;
761 delta_y = plot_maximum_y - plot_minimum_y;
762 maximum_x = plot_maximum_x + (delta_x * 0.2);
763 maximum_y = plot_maximum_y + (delta_y * 0.2);
764 minimum_x = plot_minimum_x - (delta_x * 0.2);
765 minimum_y = plot_minimum_y - (delta_y * 0.2);
766 delta_x = maximum_x - minimum_x;
767 delta_y = maximum_y - minimum_y;
768 if ((delta_y / delta_x) > (((double)height) / ((double)width)))
771 = (delta_y * ((double)width)) / ((double)height);
772 minimum_x -= (new_delta_x - delta_x) / 2.0;
773 maximum_x += (new_delta_x - delta_x) / 2.0;
778 = (delta_x * ((double)height)) / ((double)width);
779 minimum_y -= (new_delta_y - delta_y) / 2.0;
780 maximum_y += (new_delta_y - delta_y) / 2.0;
782 XCopyArea (display, pixmap, window, context, 0, 0, width, height,
784 if (delay) sleep (delay);
785 screenhack_handle_events (display);
787 if (pause) sleep (pause);
788 erase_full_window (display, window);