/* * mand.c */ #include #include #include #include #include #include #include "engine.h" #include "colors.h" #include "mand.h" /********************************************************************** * Forward Declarations **********************************************************************/ static void readCommandLine(Clp **clp, char *argv[]); static void stealth(int exitVal); static void computePerf(void); static void restartEngine(Mand *m); static int calcMaxZoom(int w, int h); static void autozoom(Mand *m, int latecomers); static void zoomIn(Mand *m); static void zoomOut(Mand *m); /********************************************************************** * Functions **********************************************************************/ int main(int argc, char *argv[]) { Mand m; int i, latecomers; time_t startTime, prevTime, currentTime; readCommandLine(&m.clp, argv); if (clp_getBool(m.clp, "version")) { printf("Backfract version " VERSION " by William Shubert - " DATE "\n"); exit(0); } if (clp_getBool(m.clp, "time")) { computePerf(); exit(0); } /* * Start up the engine! */ if (clp_getBool(m.clp, "stealth")) stealth(0); nice(clp_getInt(m.clp, "nice")); m.rnd = rnd_create(time(NULL)); io_init(&m.io, clp_getStr(m.clp, "display"), clp_getInt(m.clp, "colors"), m.rnd); m.maxZoom = calcMaxZoom(m.io.w, m.io.h); restartEngine(&m); m.result = wms_malloc(m.io.w * 3 * sizeof(UInt8)); m.delay = clp_getInt(m.clp, "delay"); m.sels = wms_malloc((m.io.h - 2) * sizeof(Selector)); prevTime = startTime = time(NULL); for (;;) { latecomers = 0; for (i = 0; i < m.io.h; ++i) { latecomers += engine(&m.e, i, m.result + (i % 3) * m.io.w); io_update(&m.io, i, m.result + (i % 3) * m.io.w); if (i > 1) { selector_read(&m.sels[i - 2], m.result + ((i - 2) % 3) * m.io.w, m.result + ((i - 1) % 3) * m.io.w, m.result + ((i ) % 3) * m.io.w, m.io.w, m.rnd); } } autozoom(&m, latecomers); if (m.delay) { currentTime = time(NULL); if ((currentTime - startTime) / m.delay == (prevTime - startTime) / m.delay) { sleep(m.delay - ((currentTime - startTime) % m.delay)); currentTime += m.delay - ((currentTime - startTime) % m.delay); } prevTime = currentTime; } } return(0); } static void readCommandLine(Clp **clp, char *argv[]) { static const ClpSetup commandLine[] = { CLPSETUP_MSG("Backfract " VERSION " (c) "DATE" William Shubert"), CLPSETUP_MSG("See \"http://www.hevanet.com/wms/comp/backfract/\" " "for more info"), CLPSETUP_MSG(""), {"version,-version", "f", "Print version information", CLPSETUP_BOOL|CLPSETUP_NOSAVE, NULL}, {"display", NULL, "Display to use", CLPSETUP_NOSAVE, NULL}, {"colors", "128", "Number of colors to use (default = 128)", CLPSETUP_SHOWARG, NULL}, {"delay", "900", "Minimum time (in seconds) between zooms (default = 900)", CLPSETUP_SHOWARG, NULL}, {"nice", "10", "Amount to lower priority (default = 10)", CLPSETUP_SHOWARG, NULL}, {"time", "f", "Don't draw anything; just time how long it takes to", CLPSETUP_BOOL, NULL}, {NULL, "", " compute a 1024x1024 image."}, {"stealth", "t", "Stealth mode", CLPSETUP_SHOWBOOL|CLPSETUP_BOOL, NULL}, CLPSETUP_END}; *clp = clp_create(commandLine); clp_rCmdline(*clp, argv); } static void stealth(int exitVal) { int pid; #ifdef TIOCNOTTY int tty; #endif pid = fork(); if (pid < 0) { perror("cgoban: fork() failed"); return; } else if (pid > 0) { /* Parent just exits. */ printf("%d\n", pid); exit(0); } /* Go stealth (ditch our controlling tty). */ #ifdef TIOCNOTTY tty = open("/dev/tty", 0); if (tty < 0) return; ioctl(tty, TIOCNOTTY, 0); close(tty); #endif } static void computePerf(void) { Engine e; struct timeval start, end; struct timezone tz; float workTime; UInt8 *result; int i; e.centerR = 0.0; e.centerI = 0.0; e.radius = 2.0; e.w = 1024; e.h = 1024; e.limit = 10000; result = wms_malloc(e.w * sizeof(UInt8)); printf("Computing a %dx%d fractal with a max depth of %d.\n", e.w, e.h, e.limit); gettimeofday(&start, &tz); for (i = 0; i < e.h; ++i) { engine(&e, i, result); } gettimeofday(&end, &tz); workTime = (float)(end.tv_sec - start.tv_sec) + 1.0e-6 * (float)(end.tv_usec - start.tv_usec); printf("Computing this set consumed %g seconds of real time.\n", workTime); } static void restartEngine(Mand *m) { m->e.centerR = 0.0; m->e.centerI = 0.0; m->e.radius = 2.0; m->e.w = m->io.w; m->e.h = m->io.h; m->e.limit = 100; m->currentZoom = 0; m->targetZoom = (rnd_int(m->rnd) % m->maxZoom) + 1; } static int calcMaxZoom(int w, int h) { /* * We must use volatile here or else the compiler can optomize out our * loop to detect the limits of our computability in such a way that * our errors show up at the wrong time! E.g., gcc on an x86 will hold * all calculations in registers that are long doubles if we don't make * these volatile. */ volatile Real x, t, two = 2.0; int numRads; if (w > h) two *= (Real)h; else two *= (Real)w; for (numRads = 0, x = 2.0;; ++numRads, x *= 0.5) { t = two + x; if (t == two) break; } return(numRads); } static void autozoom(Mand *m, int latecomers) { if (latecomers * 400 > m->e.w * m->e.h) { m->e.limit *= 2; } else if (latecomers * 2000 < m->e.w * m->e.h) { m->e.limit /= 2; if (m->e.limit < 100) m->e.limit = 100; } if (m->currentZoom == m->targetZoom) { if ((m->currentZoom != 0) && ((rnd_int(m->rnd) % 10) == 0)) { m->targetZoom = 0; } else { m->targetZoom = rnd_int(m->rnd) % (m->maxZoom - 1); if (m->targetZoom >= m->currentZoom) ++m->targetZoom; } io_recolor(&m->io, m->rnd); } if (m->targetZoom > m->currentZoom) { zoomIn(m); } else { zoomOut(m); } } static void zoomIn(Mand *m) { int x, y, w = m->e.w, h = m->e.h; Real pixelStep; if (w > h) pixelStep = m->e.radius / (Real)(h >> 1); else pixelStep = m->e.radius / (Real)(w >> 1); x = w; y = h; selector_pick(&x, &y, m->sels, h - 2, m->rnd); m->e.centerR += (Real)(x - (w/2)) * pixelStep; m->e.centerI += (Real)(y - (h/2)) * pixelStep; m->e.radius *= 0.5; ++m->currentZoom; return; } static void zoomOut(Mand *m) { m->e.radius *= 2.0; --m->currentZoom; }