/* $Id: chaleurgraphe2.c,v 1.3 2008/06/12 17:22:06 minh Exp minh $ */ /*- * Copyright (c) 2008 Nhat Minh LĂȘ * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include /* Core stuff */ #ifndef MAXWIDTH #define MAXWIDTH 400 #endif #ifndef MAXHEIGHT #define MAXHEIGHT MAXWIDTH #endif #ifndef PRECISION #define PRECISION (DBL_EPSILON * 100.0) #endif /* XXX: savebmp() code relies on this hard-coded value to decide length of file name buffer */ #define MAXMAXITER 1000000U struct cell { double value; unsigned disabled: 1; }; struct state { struct cell *matrix; double maxval; unsigned int width, height; unsigned int niter; }; static double precision = PRECISION; static unsigned int maxiter = 100, refreshmod; static unsigned int oprec = 6; static int quietmode; static char *inisnapshotname, *snapshotname; static size_t snapshotnamelen; /* Graph stuff */ struct color { unsigned red: 8; unsigned green: 8; unsigned blue: 8; }; static SDL_Surface *screen, *canvas; static struct color basecolor = { 255, 0, 0 }; /* Utilities */ static void error(const char *fmt, ...) { va_list ap; fprintf(stderr, "error: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); exit(EXIT_FAILURE); } static void *xmalloc(size_t n) { void *p; if ((p = malloc(n)) == NULL) error(0, "out of memory"); return p; } /* Operations */ #define PT(m, w, x, y) (&(m)[(x)*(w) + (y)]) #define GETPT(m, w, x, y) ((m)[(x)*(w) + (y)].value) #define SETPT(m, w, x, y, v) ((m)[(x)*(w) + (y)].value = (v)) static struct state *clonestate(struct state *src) { struct state *s; unsigned int msize; s = xmalloc(sizeof *s); s->width = src->width, s->height = src->height; msize = src->width * src->height; s->matrix = xmalloc(sizeof *s->matrix * msize); memcpy(s->matrix, src->matrix, sizeof *s->matrix * msize); s->niter = 0; return s; } static void freestate(struct state *s) { free(s->matrix); free(s); } static int cmpmatrixes(struct cell *a, struct cell *b, unsigned int w, unsigned int h) { unsigned int x, y; double r; for (x = 0; x < h; ++x) for (y = 0; y < w; ++y) { r = GETPT(a, w, x, y) - GETPT(b, w, x, y); if (r < -precision) return -1; else if (r > precision) return 1; } return 0; } static int cmpstates(struct state *a, struct state *b) { return cmpmatrixes(a->matrix, b->matrix, a->width, b->width); } /* Loader */ static struct cell *loadmatrix(FILE *fp, unsigned int w, unsigned int h, double *maxptr) { struct cell *m; double max, val; unsigned int x, y; max = 0.0; m = xmalloc(sizeof *m * w * h); for (x = 0; x < h; ++x) for (y = 0; y < w; ++y) { if (fscanf(fp, "%lf", &val) < 1) error("syntax error in matrix definition"); SETPT(m, w, x, y, val); PT(m, w, x, y)->disabled = 0; if (val > max) max = val; } if (maxptr != NULL) *maxptr = max; return m; } static struct state *loadstate(FILE *fp) { struct state *s; double max; unsigned int w, h; s = xmalloc(sizeof *s); if (fscanf(fp, "%u %u", &w, &h) < 2) error("syntax error in matrix declaration"); if (w < 2 || h < 2) error("matrix is too small (min: 2x2)"); if (w > MAXWIDTH || h > MAXHEIGHT) error("matrix is too large (max: %ux%u)", MAXWIDTH, MAXHEIGHT); s->width = w, s->height = h; s->matrix = loadmatrix(fp, w, h, &max); s->maxval = max; s->niter = 0; return s; } /* Text output */ static void printmatrix(struct cell *m, unsigned int w, unsigned int h) { unsigned int x, y; for (x = 0; x < h; ++x) { for (y = 0; y < w; ++y) printf("%.*f\t", (int)oprec, GETPT(m, w, x, y)); printf("\n"); } } static void printstate(struct state *s) { printf("#%u:\n", s->niter); if (!quietmode) printmatrix(s->matrix, s->width, s->height); } /* Graphical rendering */ static Uint32 gradtosdlpix(struct state *s, SDL_Surface *sur, struct color *c, double val) { return SDL_MapRGB(sur->format, c->red * val / s->maxval, c->green * val / s->maxval, c->blue * val / s->maxval); } static void update(struct state *s) { Uint32 *pixs; double x; unsigned int i, msize; if (SDL_LockSurface(canvas) != 0) error("unable to lock canvas"); pixs = canvas->pixels; for (i = 0, msize = s->width * s->height; i < msize; ++i) { x = s->matrix[i].value; pixs[i] = gradtosdlpix(s, canvas, &basecolor, x); } SDL_UnlockSurface(canvas); if (SDL_BlitSurface(canvas, NULL, screen, NULL) != 0) error("unable to blit canvas to screen"); SDL_UpdateRect(screen, 0, 0, 0, 0); } static struct cell *loadmatrixbmp(SDL_Surface *bmp, double *maxptr) { Uint32 *pixs; struct cell *m; double max, val; unsigned int w, h; unsigned int i, msize; Uint8 r, g, b; w = bmp->w, h = bmp->h; max = 0.0; m = xmalloc(sizeof *m * w * h); if (SDL_LockSurface(bmp) != 0) error("unable to lock surface"); pixs = bmp->pixels; for (i = 0, msize = w * h; i < msize; ++i) { SDL_GetRGB(pixs[i], bmp->format, &r, &g, &b); val = r; m[i].value = val; m[i].disabled = 0; if (val > max) max = val; } SDL_UnlockSurface(bmp); if (maxptr != NULL) *maxptr = max; return m; } static struct state *loadbmp(const char *filename) { SDL_Surface *bmp, *sur; struct state *s; if ((bmp = SDL_LoadBMP(filename)) == NULL) error("unable to load source bitmap"); if ((sur = SDL_CreateRGBSurface(SDL_SWSURFACE, bmp->w, bmp->h, 32, 0, 0, 0, 0)) == NULL) error("unable to create SDL surface"); if (SDL_BlitSurface(bmp, NULL, sur, NULL) != 0) error("unable to blit source to surface"); SDL_FreeSurface(bmp); s = xmalloc(sizeof *s); s->width = sur->w, s->height = sur->h; s->matrix = loadmatrixbmp(sur, &s->maxval); s->niter = 0; SDL_FreeSurface(sur); return s; } static void savebmp(struct state *s) { char *buf; /* XXX: put the allocation elsewhere */ buf = xmalloc(snapshotnamelen + 7 + 4 + 1); sprintf(buf, "%s%07u.bmp", snapshotname, s->niter); SDL_SaveBMP(canvas, buf); free(buf); } static void initrend(struct state *s) { if ((screen = SDL_SetVideoMode(s->width, s->height, 24, SDL_SWSURFACE)) == NULL) error("unable to create SDL screen surface"); if ((canvas = SDL_CreateRGBSurface(SDL_SWSURFACE, s->width, s->height, 32, 0, 0, 0, 0)) == NULL) error("unable to create SDL canvas surface"); } static void finirend(void) { SDL_FreeSurface(canvas); } /* Diffusion */ static void diffusematrix(struct cell *m, struct cell *src, unsigned int w, unsigned int h) { unsigned int x, y; for (x = 1; x < h-1; ++x) for (y = 1; y < w-1; ++y) if (!PT(src, w, x, y)->disabled) SETPT(m, w, x, y, (GETPT(src, w, x-1, y-1) + GETPT(src, w, x-1, y+1) + GETPT(src, w, x+1, y-1) + GETPT(src, w, x+1, y+1)) / 4); } static void diffuse(struct state *s) { struct cell *m; m = xmalloc(sizeof *m * s->width * s->height); memcpy(m, s->matrix, sizeof *m * s->width * s->height); diffusematrix(m, s->matrix, s->width, s->height); free(s->matrix); s->matrix = m; ++s->niter; } /* Main program */ static void usage(void) { fprintf(stderr, "Usage:\n%s%s%s%s%s%s%s%s", "\tchaleur [OPTIONS]\n", "-D prec output precision\n", "-R modulo text output every modulo passes\n", "-n maxiter limit on the number of iterations\n", "-p prec requested precision\n", "-q be quiet\n", "-r file read snapshot\n", "-s name make snapshots\n"); } static char *optarg(char ***argvptr) { if ((*argvptr)[1] != NULL) return *++*argvptr; else return NULL; } static void parseopts(int *argcptr, char ***argvptr) { char *arg; --*argcptr; ++*argvptr; while (**argvptr != NULL && (**argvptr)[0] == '-') { switch ((**argvptr)[1]) { case 'D': if ((arg = optarg(argvptr)) != NULL) oprec = strtoul(arg, NULL, 0); break; case 'R': if ((arg = optarg(argvptr)) != NULL) refreshmod = strtoul(arg, NULL, 0); break; case 'n': if ((arg = optarg(argvptr)) != NULL) maxiter = strtoul(arg, NULL, 0); if (maxiter > MAXMAXITER) { fprintf(stderr, "warning: maximum number of " "iterations exceeds hard limit\n"); maxiter = MAXMAXITER; } break; case 'p': if ((arg = optarg(argvptr)) != NULL) precision = strtod(arg, NULL); break; case 'q': quietmode = 1; break; case 'r': if ((arg = optarg(argvptr)) != NULL) inisnapshotname = arg; break; case 's': if ((arg = optarg(argvptr)) != NULL) { snapshotname = arg; snapshotnamelen = strlen(arg); } break; default: usage(); exit(0); } --*argcptr; ++*argvptr; } } int main(int argc, char *argv[]) { struct state *s, *t; Uint32 lasttime, curtime; unsigned int i; parseopts(&argc, &argv); if (refreshmod == 0 || refreshmod > maxiter) refreshmod = maxiter / 10; SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); atexit(SDL_Quit); if (inisnapshotname == NULL) s = loadstate(stdin); else s = loadbmp(inisnapshotname); t = clonestate(s); initrend(s); atexit(finirend); printf("%u %u %f\n", s->width, s->height, s->maxval); update(s); lasttime = SDL_GetTicks(); for (i = 0; i < maxiter; ++i) { curtime = SDL_GetTicks(); if (lasttime + 33 < curtime){ update(s); lasttime = curtime; } if (i % refreshmod == 0) { if (snapshotname != NULL) savebmp(s); printstate(s); } diffuse(s); diffuse(t), diffuse(t); if (cmpstates(s, t) == 0) break; } update(s); if (snapshotname != NULL) savebmp(s); printstate(s); SDL_Delay(1000); /* XXX: not called on error exit */ freestate(s); freestate(t); return 0; }