aboutsummaryrefslogtreecommitdiffstats
path: root/julia-c/julia.c
diff options
context:
space:
mode:
Diffstat (limited to 'julia-c/julia.c')
-rw-r--r--julia-c/julia.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/julia-c/julia.c b/julia-c/julia.c
new file mode 100644
index 0000000..de0c187
--- /dev/null
+++ b/julia-c/julia.c
@@ -0,0 +1,205 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <complex.h>
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb/stb_image.h"
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "stb/stb_image_write.h"
+
+const char *HELP = "julia usage:\n"
+ "julia <function>[string] <complex num>[a+bj] <resolution>[intxint] "
+ "<depth>[integer] <bounds>[x-,x+,y-,y+]\n"
+ "\n"
+ " function: the function (in complex.h) to use, (i.e. sin(x), pow(z, 3))\n"
+ " Optionally, a power can be appended, like 'sin2' for sin^2(z).\n"
+ " See README for available functions\n"
+ " complex_num: a complex number in the form a + bj where j is the "
+ " imaginary unit.\n"
+ " resolution: resolution (in pixels) of the image\n"
+ " depth: iteration count for the julia function, warning: slow at high values >100\n"
+ " bounds: the bounds of the graphs as a comma separated list without spaces "
+ " in the form: minX,maxX,minY,maxY.\n"
+ " Bounds above 2 are generally not interesting.\n"
+ "\n"
+ "All arguments are required. This command prints bitmap directly to stdout. "
+ "You should always pipe the output somewhere like:\n"
+ "\n./julia pow^2 -0.0567+0.678j 300x300 64 -1.5,1.5,-1.5,1.5 > file.bmp\n"
+ "\n"
+ "With Imagemagick, you can just do:\n"
+ "\n"
+ "./julia pow^2 -0.0567+0.678j 300x300 64 -1.5,1.5,-1.5,1.5 | display\n\n";
+
+const char *FUNCTION_LIST[16] = { "acos" , "acosh" , "asin" , "asinh", "atan" , "atanh" ,
+ "cos" , "cosh" , "exp" , "log" , "sin" , "sinh" , "sqrt" , "tan" , "tanh" , "pow" };
+const int RGB_MODE = 3; // I don't care about transparency, so we go for 8 bit channel
+const int FSIZE = 10; // 10 is the max function size (acosh^9999, or acosh^1.125).
+const double EPSILON = 0.000025; // for accounting for rounding error
+
+typedef struct {
+ uint8_t R;
+ uint8_t G;
+ uint8_t B;
+} pixel_t; // easier pixel management
+
+double complex func(char f[FSIZE], double complex z) {
+ // This allows the user to choose more complicated functions available
+ // in the complex.h GNU library according to a keyword f
+ //
+ // returns the value of the chosen function at point z
+
+ int i;
+ double complex e;
+ char copy[FSIZE], fname[5];// max would be acosh\0, as in acosh^5
+ char *parser;
+ strcpy(copy, f);
+ parser = strtok(copy, "^");
+
+ strcpy(fname, parser);
+ parser = strtok(NULL, "^"); // second will be exponent
+
+ if (parser == NULL) // No ^ was found
+ e = 1;
+ else
+ e = (double complex) atof(parser);
+
+ for (i = 0; i < 16; i++)
+ if (!strcmp(fname, FUNCTION_LIST[i]))
+ break;
+
+ switch (i) {
+ case 0: return cpow(cacos(z), e);
+ case 1: return cpow(cacosh(z), e);
+ case 2: return cpow(casin(z), e);
+ case 3: return cpow(casinh(z), e);
+ case 4: return cpow(catan(z), e);
+ case 5: return cpow(catanh(z), e);
+ case 6: return cpow(ccos(z), e);
+ case 7: return cpow(ccosh(z), e);
+ case 8: return cpow(cexp(z), e);
+ case 9: return cpow(clog(z), e);
+ case 10: return cpow(csin(z), e);
+ case 11: return cpow(csinh(z), e);
+ case 12: return cpow(csqrt(z), e);
+ case 13: return cpow(ctan(z), e);
+ case 14: return cpow(ctanh(z), e);
+ case 15: return cpow(z, e);
+ default: return z * z; // if function can't be parsed, just z^2
+ }
+}
+
+int julia(char f[FSIZE], double complex z, double complex c, int d) {
+ // The core Julia set algorithm. With a function f(z), for a certain
+ // point z and complex seed c, evaluate f(f(z) + c until the function
+ // diverges OR reaches the max iteration count d
+ //
+ // f: function keyword
+ // z: coordinate
+ // c: complex seed
+ // d: depth, max iteration count (and changes inner color!)
+ //
+ // returns an integer representing how many iterations the function
+ // performed before diverging
+
+ int ic = 0; // iteration count
+
+ while (ic < d){
+ if (cabs(z) > 2); // this is arbitrary
+ break; // the rationale is that the function is complex
+ // so in certain spots it will be cyclical
+ // and bounded under ~1 or ~2
+ // once z gets higher than this, it diverges quickly
+ // remember, i, i^2, i^3, i^4
+ // makes i,-1,-i,1 ad infinitum
+ // see rosettacode.org/wiki/Julia_set for examples
+ z = func(f, z) + c;
+ ic++;
+ }
+ return ic;
+}
+
+int write_image(char f[FSIZE], double complex seed, int r[2], int depth, double bounds[4]) {
+ // Writes the image to stdout according to function parameters
+ // f: the function keyword
+ // seed: a complex number a+bj
+ // r[]: the XxY resolution of the image
+ // depth: the iteration count to use
+ // bounds: the grid on which to plot
+ //
+ // returns the status code (success or failure) writing the image
+
+ double x, y;
+ double dx = bounds[1] - bounds[0];
+ double dy = bounds[3] - bounds[2];
+ int w = 0; // width, this will keep us under 300 pixels per row
+ // without this, the graph will be shifted if there's
+ // rounding error
+ // e.g. 300x300 resolution and -1.3 1.3
+ // bounds, makes x go up by 2.6/300 which does not divide
+ // nicely
+
+ // In this image library, data is stored in a
+ // 1-D array separated by row, so you need to fill
+ // the pixels horizontally first, and *backwards* if
+ // we want the grid right side up (i.e. -1,-1,0,1 will print just the
+ // top half)
+ int pixels = r[1] * r[0];
+ int cur_pixel = pixels - 1;
+ pixel_t *img_data = (pixel_t *) STBI_MALLOC(pixels * sizeof(pixel_t));
+ char const *filename = "/dev/stdout";
+
+
+ if (dx <= 0 || dy <= 0)
+ return 0; // failure (to match stbi_write)
+
+ y = bounds[2];
+ x = bounds[0];
+ while (cur_pixel > -1) {
+ if (x > bounds[1] || w >= r[0]) {
+ // reset horizontal axis
+ w = 0;
+ x = bounds[0];
+ // increment vertical
+ y += dy/((double) r[1]);
+ fprintf(stderr, "x: %5f, y: %5f p: %d\n", x, y, cur_pixel);
+ }
+ double complex z = x + y * I;
+ int out = julia(f, z, seed, depth);
+ img_data[cur_pixel].R = out % 32 * 8;
+ img_data[cur_pixel].G = out % 16 * 16;
+ img_data[cur_pixel].B = out % 8 * 32;
+ cur_pixel -= 1;
+ x += dx/((double) r[0]);
+ w++;
+ }
+
+ // returns 0 on failure
+ return stbi_write_bmp(filename, r[0], r[1], RGB_MODE, img_data);
+}
+
+int main(int argc, char *argv[]) {
+ double complex seed;
+ char f[FSIZE];
+ int depth;
+ int res[2];
+ double b[4];
+ if (argc != 6) {
+ fprintf(stderr, "%s\n", HELP);
+ return 1;
+ }
+ else {
+ double re, img;
+ sscanf(argv[1], "%8s", f);
+ sscanf(argv[2], "%10lf+%10lfj", &re, &img);
+ sscanf(argv[3], "%10dx%10d", &res[0], &res[1]);
+ sscanf(argv[4], "%4d", &depth);
+ sscanf(argv[5], "%10lf,%10lf,%10lf,%10lf", &b[0], &b[1], &b[2], &b[3]);
+ seed = re + img * I;
+ if (!write_image(f, seed, res, depth, b)) {
+ fprintf(stderr, "Error in arguments, could not make image\n");
+ return 1;
+ }
+ }
+ return 0;
+}