The repository formerly known as dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

brot.c 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <complex.h>
  17. #include <err.h>
  18. #include <math.h>
  19. #include <stdbool.h>
  20. #include <stdint.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sysexits.h>
  25. #include <time.h>
  26. #include <unistd.h>
  27. #include "gfx.h"
  28. static uint32_t rgb(uint8_t r, uint8_t g, uint8_t b) {
  29. return (uint32_t)r << 16 | (uint32_t)g << 8 | (uint32_t)b;
  30. }
  31. static uint32_t gray(uint8_t n) {
  32. return rgb(n, n, n);
  33. }
  34. static double absSq(double complex z) {
  35. return creal(z) * creal(z) + cimag(z) * cimag(z);
  36. }
  37. static uint32_t depth = 50;
  38. static uint32_t mandelbrot(double complex c) {
  39. double complex z = 0;
  40. for (uint32_t i = 0; i < depth; ++i) {
  41. if (absSq(z) > 4.0) return i;
  42. z = z * z + c;
  43. }
  44. return 0;
  45. }
  46. static double complex translate = -0.75;
  47. static double complex transform = 2.5;
  48. static uint32_t samples = 1;
  49. static void sample(uint32_t *buf, size_t width, size_t height) {
  50. double yRatio = (height > width) ? (double)height / (double)width : 1.0;
  51. double xRatio = (width > height) ? (double)width / (double)height : 1.0;
  52. memset(buf, 0, 4 * width * height);
  53. size_t superWidth = width * samples;
  54. size_t superHeight = height * samples;
  55. for (size_t y = 0; y < superHeight; ++y) {
  56. for (size_t x = 0; x < superWidth; ++x) {
  57. double zx = (((double)x + 0.5) / (double)superWidth - 0.5) * xRatio;
  58. double zy = (((double)y + 0.5) / (double)superHeight - 0.5) * yRatio;
  59. uint32_t n = mandelbrot((zx + zy * I) * transform + translate);
  60. buf[(y / samples) * width + (x / samples)] += n;
  61. }
  62. }
  63. }
  64. static void color(uint32_t *buf, size_t width, size_t height) {
  65. for (size_t i = 0; i < width * height; ++i) {
  66. buf[i] = gray(255 * buf[i] / samples / samples / depth);
  67. }
  68. }
  69. static double frameTime;
  70. void draw(uint32_t *buf, size_t width, size_t height) {
  71. clock_t t0 = clock();
  72. sample(buf, width, height);
  73. color(buf, width, height);
  74. frameTime = (double)(clock() - t0) / (double)CLOCKS_PER_SEC;
  75. }
  76. static double translateStep = 1.0 / 128.0;
  77. static double rotateStep = 1.0 / 128.0;
  78. static double scaleStep = 1.0 / 32.0;
  79. bool input(char in) {
  80. const double PI = acos(-1.0);
  81. switch (in) {
  82. break; case 'q': return false;
  83. break; case '.': depth++;
  84. break; case ',': if (depth > 1) depth--;
  85. break; case 'l': translate += translateStep * transform;
  86. break; case 'h': translate -= translateStep * transform;
  87. break; case 'j': translate += translateStep * I * transform;
  88. break; case 'k': translate -= translateStep * I * transform;
  89. break; case 'u': transform *= cexp(rotateStep * PI * I);
  90. break; case 'i': transform /= cexp(rotateStep * PI * I);
  91. break; case '+': transform *= 1.0 - scaleStep;
  92. break; case '-': transform /= 1.0 - scaleStep;
  93. break; case '0': translate = -0.75; transform = 2.5;
  94. break; case ']': samples++;
  95. break; case '[': if (samples > 1) samples--;
  96. }
  97. return true;
  98. }
  99. const char *status(void) {
  100. static char buf[256];
  101. snprintf(
  102. buf, sizeof(buf),
  103. "brot -s %u -i %u -t %g%+gi -f %g%+gi # %.6f",
  104. samples, depth,
  105. creal(translate), cimag(translate),
  106. creal(transform), cimag(transform),
  107. frameTime
  108. );
  109. return buf;
  110. }
  111. static double complex parseComplex(const char *str) {
  112. double real = 0.0, imag = 0.0;
  113. real = strtod(str, (char **)&str);
  114. if (str[0] == 'i') {
  115. imag = real;
  116. real = 0.0;
  117. } else if (str[0]) {
  118. imag = strtod(str, NULL);
  119. }
  120. return real + imag * I;
  121. }
  122. int init(int argc, char *argv[]) {
  123. int opt;
  124. while (0 < (opt = getopt(argc, argv, "f:i:s:t:"))) {
  125. switch (opt) {
  126. break; case 'f': transform = parseComplex(optarg);
  127. break; case 'i': depth = strtoul(optarg, NULL, 0);
  128. break; case 's': samples = strtoul(optarg, NULL, 0);
  129. break; case 't': translate = parseComplex(optarg);
  130. break; default: return EX_USAGE;
  131. }
  132. }
  133. return EX_OK;
  134. }