/* * fractal.c - command-line escape-time fractal renderer that writes GIFs. * * DeepSeek on Obsidian. Portable C89; no libraries beyond libm. * * fractal -w 320 -h 240 -x -0.5 -y 0 -s 3 -i 256 -p fire -o out.gif * * Author: DeepSeek (deepseek-v4.1-flash), October 2026. */ #include #include #include #include #include "render.h" static const char *progname = "fractal"; static void usage(FILE *f) { fprintf(f, "usage: %s [options] [-o outfile]\n", progname); fprintf(f, " -w N width in pixels (default 320)\n"); fprintf(f, " -h N height in pixels (default 240)\n"); fprintf(f, " -x F centre, real part (default -0.5)\n"); fprintf(f, " -y F centre, imaginary part (default 0)\n"); fprintf(f, " -s F view width, complex plane (default 3.0)\n"); fprintf(f, " -i N maximum iterations (default 256)\n"); fprintf(f, " -f TYPE mandel | julia (default mandel)\n"); fprintf(f, " -jr F Julia constant, real (default -0.4)\n"); fprintf(f, " -ji F Julia constant, imag (default 0.6)\n"); fprintf(f, " -p NAME gray|fire|ocean|rainbow (default fire)\n"); fprintf(f, " -m NAME linear | cycle (default cycle)\n"); fprintf(f, " -c F palette cycles over range (default 6.0)\n"); fprintf(f, " -o FILE output file (default stdout)\n"); fprintf(f, " -q quiet\n"); fprintf(f, " -? print this help\n"); } static int is_help(const char *s) { return strcmp(s, "-?") == 0 || strcmp(s, "--help") == 0; } int main(int argc, char **argv) { FractalParams p; FILE *out = stdout; const char *outfile = (const char *)0; int quiet = 0; int i; clock_t t0, t1; fractal_defaults(&p); for (i = 1; i < argc; i++) { const char *a = argv[i]; if (is_help(a)) { usage(stdout); return 0; } if (strcmp(a, "-q") == 0) { quiet = 1; continue; } if (strcmp(a, "-w") == 0 && i + 1 < argc) { p.width = atoi(argv[++i]); continue; } if (strcmp(a, "-h") == 0 && i + 1 < argc) { p.height = atoi(argv[++i]); continue; } if (strcmp(a, "-x") == 0 && i + 1 < argc) { p.center_re = atof(argv[++i]); continue; } if (strcmp(a, "-y") == 0 && i + 1 < argc) { p.center_im = atof(argv[++i]); continue; } if (strcmp(a, "-s") == 0 && i + 1 < argc) { p.span = atof(argv[++i]); continue; } if (strcmp(a, "-i") == 0 && i + 1 < argc) { p.max_iter = atoi(argv[++i]); continue; } if (strcmp(a, "-jr") == 0 && i + 1 < argc) { p.julia_re = atof(argv[++i]); continue; } if (strcmp(a, "-ji") == 0 && i + 1 < argc) { p.julia_im = atof(argv[++i]); continue; } if (strcmp(a, "-c") == 0 && i + 1 < argc) { p.cycle = atof(argv[++i]); continue; } if (strcmp(a, "-o") == 0 && i + 1 < argc) { outfile = argv[++i]; continue; } if (strcmp(a, "-f") == 0 && i + 1 < argc) { const char *v = argv[++i]; if (strcmp(v, "julia") == 0) p.julia = 1; else p.julia = 0; continue; } if (strcmp(a, "-p") == 0 && i + 1 < argc) { const char *v = argv[++i]; if (strcmp(v, "gray") == 0) p.palette = PAL_GRAY; else if (strcmp(v, "ocean") == 0) p.palette = PAL_OCEAN; else if (strcmp(v, "rainbow") == 0) p.palette = PAL_RAINBOW; else p.palette = PAL_FIRE; continue; } if (strcmp(a, "-m") == 0 && i + 1 < argc) { const char *v = argv[++i]; p.map = (strcmp(v, "linear") == 0) ? MAP_LINEAR : MAP_CYCLE; continue; } fprintf(stderr, "%s: unknown or incomplete option '%s'\n", progname, a); usage(stderr); return 2; } if (p.width <= 0 || p.height <= 0 || p.max_iter <= 0 || p.span <= 0.0) { fprintf(stderr, "%s: bad dimensions or parameters\n", progname); return 2; } if (outfile != (const char *)0) { out = fopen(outfile, "wb"); if (out == (FILE *)0) { fprintf(stderr, "%s: cannot open '%s'\n", progname, outfile); return 1; } } t0 = clock(); if (fractal_render(out, &p) != 0) { fprintf(stderr, "%s: render failed\n", progname); if (out != stdout) fclose(out); return 1; } t1 = clock(); if (out != stdout) fclose(out); if (!quiet) { double secs = (double)(t1 - t0) / (double)CLOCKS_PER_SEC; long px = (long)p.width * (long)p.height; fprintf(stderr, "%dx%d iter=%d map=%d pal=%d : %.3f s cpu, %.0f px/s\n", p.width, p.height, p.max_iter, p.map, p.palette, secs, secs > 0.0 ? (double)px / secs : 0.0); } return 0; }