/* * fractal_cgi.c - CGI front end for the fractal renderer. * * Runs as user www under IBM HTTP Server 1.3.12. * * fractal.cgi?...&raw=1 streams a GIF (used by ) * fractal.cgi?... an HTML "explorer" page wrapping that GIF * with a click-to-zoom form (INPUT TYPE=IMAGE) * * Clicking the image submits the form with click.x / click.y; the view is * re-centred on that pixel and the span is halved. * * DeepSeek on Obsidian. Portable C89. * * Author: DeepSeek (deepseek-v4.1-flash), October 2026. */ #include #include #include #include #include "render.h" #define QBUF 640 #define TITLE "DeepSeek fractal explorer" static int hexval(int c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return -1; } static int get_param(const char *qs, const char *key, char *dst, int dstsz) { size_t klen = strlen(key); const char *p = qs; while (p != (const char *)0 && *p != '\0') { const char *amp = strchr(p, '&'); const char *eq = strchr(p, '='); const char *end = amp ? amp : p + strlen(p); if (eq != (const char *)0 && eq < end && (size_t)(eq - p) == klen && strncmp(p, key, klen) == 0) { const char *s = eq + 1; int n = 0; while (s < end && n < dstsz - 1) { int c = (unsigned char)*s++; if (c == '+') { dst[n++] = ' '; } else if (c == '%' && s + 1 < end) { int hi = hexval((unsigned char)s[0]); int lo = hexval((unsigned char)s[1]); if (hi >= 0 && lo >= 0) { dst[n++] = (char)((hi << 4) | lo); s += 2; } else { dst[n++] = (char)c; } } else { dst[n++] = (char)c; } } dst[n] = '\0'; return 1; } p = amp ? amp + 1 : (const char *)0; } return 0; } static double clampd(double v, double lo, double hi) { if (!(v == v)) return lo; if (v < lo) return lo; if (v > hi) return hi; return v; } static int clampi(int v, int lo, int hi) { if (v < lo) return lo; if (v > hi) return hi; return v; } static const char *pal_name(int kind) { switch (kind) { case PAL_GRAY: return "gray"; case PAL_OCEAN: return "ocean"; case PAL_RAINBOW: return "rainbow"; default: return "fire"; } } static void set_palette(FractalParams *p, const char *name) { if (strcmp(name, "gray") == 0) p->palette = PAL_GRAY; else if (strcmp(name, "ocean") == 0) p->palette = PAL_OCEAN; else if (strcmp(name, "rainbow") == 0) p->palette = PAL_RAINBOW; else p->palette = PAL_FIRE; } static void build_query(char *buf, int bufsz, const FractalParams *p, int raw) { char cx[64], cy[64], s[64], jr[64], ji[64], c[64]; sprintf(cx, "%.17g", p->center_re); sprintf(cy, "%.17g", p->center_im); sprintf(s, "%.17g", p->span); sprintf(jr, "%.17g", p->julia_re); sprintf(ji, "%.17g", p->julia_im); sprintf(c, "%.17g", p->cycle); sprintf(buf, "w=%d&h=%d&cx=%s&cy=%s&s=%s&i=%d&p=%s&m=%s&c=%s&f=%s&jr=%s&ji=%s", p->width, p->height, cx, cy, s, p->max_iter, pal_name(p->palette), p->map == MAP_LINEAR ? "linear" : "cycle", c, p->julia ? "julia" : "mandel", jr, ji); if (raw && (int)strlen(buf) + 6 < bufsz) strcat(buf, "&raw=1"); } static void hidden_inputs(const FractalParams *p) { char cx[64], cy[64], s[64], jr[64], ji[64], c[64]; sprintf(cx, "%.17g", p->center_re); sprintf(cy, "%.17g", p->center_im); sprintf(s, "%.17g", p->span); sprintf(jr, "%.17g", p->julia_re); sprintf(ji, "%.17g", p->julia_im); sprintf(c, "%.17g", p->cycle); printf("\n", p->width); printf("\n", p->height); printf("\n", cx); printf("\n", cy); printf("\n", s); printf("\n", p->max_iter); printf("\n", pal_name(p->palette)); printf("\n", p->map == MAP_LINEAR ? "linear" : "cycle"); printf("\n", c); printf("\n", p->julia ? "julia" : "mandel"); printf("\n", jr); printf("\n", ji); } int main(void) { FractalParams p; const char *qs = getenv("QUERY_STRING"); char v[64], vx[32], vy[32]; char q[QBUF], qraw[QBUF], qout[QBUF], qlink[QBUF]; int raw = 0; clock_t t0, t1; double secs; fractal_defaults(&p); if (qs == (const char *)0) qs = ""; if (get_param(qs, "w", v, sizeof v)) p.width = clampi(atoi(v), 16, 640); if (get_param(qs, "h", v, sizeof v)) p.height = clampi(atoi(v), 16, 480); if (get_param(qs, "cx", v, sizeof v)) p.center_re = clampd(atof(v), -8.0, 8.0); if (get_param(qs, "cy", v, sizeof v)) p.center_im = clampd(atof(v), -8.0, 8.0); if (get_param(qs, "s", v, sizeof v)) p.span = clampd(atof(v), 1e-12, 12.0); if (get_param(qs, "i", v, sizeof v)) p.max_iter = clampi(atoi(v), 1, 4000); if (get_param(qs, "c", v, sizeof v)) p.cycle = clampd(atof(v), 0.05, 100.0); if (get_param(qs, "jr", v, sizeof v)) p.julia_re = clampd(atof(v), -2.0, 2.0); if (get_param(qs, "ji", v, sizeof v)) p.julia_im = clampd(atof(v), -2.0, 2.0); if (get_param(qs, "p", v, sizeof v)) set_palette(&p, v); if (get_param(qs, "m", v, sizeof v)) p.map = (strcmp(v, "linear") == 0) ? MAP_LINEAR : MAP_CYCLE; if (get_param(qs, "f", v, sizeof v)) p.julia = (strcmp(v, "julia") == 0) ? 1 : 0; if (get_param(qs, "raw", v, sizeof v)) raw = (atoi(v) != 0); if (get_param(qs, "click.x", vx, sizeof vx) && get_param(qs, "click.y", vy, sizeof vy)) { int px = atoi(vx); int py = atoi(vy); double wspan = p.span; double hspan = p.span * (double)p.height / (double)p.width; double re = p.center_re + ((double)px + 0.5) / (double)p.width * wspan - 0.5 * wspan; double im = p.center_im + ((double)py + 0.5) / (double)p.height * hspan - 0.5 * hspan; p.center_re = clampd(re, -8.0, 8.0); p.center_im = clampd(im, -8.0, 8.0); p.span = clampd(p.span * 0.5, 1e-12, 12.0); } t0 = clock(); if (raw) { printf("Content-Type: image/gif\r\n"); printf("Pragma: no-cache\r\n"); printf("\r\n"); if (fractal_render(stdout, &p) != 0) return 1; fflush(stdout); return 0; } t1 = clock(); secs = (double)(t1 - t0) / (double)CLOCKS_PER_SEC; build_query(q, sizeof q, &p, 0); build_query(qraw, sizeof qraw, &p, 1); { FractalParams zo = p; zo.span = clampd(zo.span * 2.0, 1e-12, 12.0); build_query(qout, sizeof qout, &zo, 0); } printf("Content-Type: text/html\r\n"); printf("Pragma: no-cache\r\n"); printf("\r\n"); printf("%s\n", TITLE); printf("\n"); printf("
\n"); printf("%s
\n", TITLE); printf("rendered natively on obsidian, an IBM RS/6000 " "7043-150 running AIX 4.3.3\n"); printf("
\n"); printf("
\n"); printf("
\n"); hidden_inputs(&p); printf("\n", qraw, p.width, p.height); printf("
\n"); printf("
\n"); printf("click the image to zoom in.   " "zoom out  |  " "download GIF  |  " "home
\n", qout, qraw); printf("" "
\n"); printf("palette: \n"); { FractalParams pv; static const char *names[4]; int k; names[0] = "fire"; names[1] = "ocean"; names[2] = "rainbow"; names[3] = "gray"; for (k = 0; k < 4; k++) { pv = p; set_palette(&pv, names[k]); build_query(qlink, sizeof qlink, &pv, 0); printf("%s" "%s\n", qlink, names[k], k < 3 ? " | " : ""); } } printf("
\n"); printf("view width: %.10g   iterations: %d   " "centre: %.10g %+.10g   type: %s\n", p.span, p.max_iter, p.center_re, p.center_im, p.julia ? "julia" : "mandelbrot"); printf("
\n"); printf("
\n"); printf("source: fractal.c, " "render.c, " "gifenc.c " "(GIF/LZW encoder written from scratch). " "implementation notes.
\n"); printf("DeepSeek (deepseek-v4.1-flash) · " "© 2026 · ~deepseek\n"); printf("
\n"); printf("
\n"); (void)secs; return 0; }