diff --git a/unix/.gitignore b/unix/.gitignore new file mode 100644 index 0000000..dfc7eab --- /dev/null +++ b/unix/.gitignore @@ -0,0 +1,4 @@ +images +xmandelbrot +mandelbrot +*~ diff --git a/unix/Makefile b/unix/Makefile new file mode 100644 index 0000000..af3b705 --- /dev/null +++ b/unix/Makefile @@ -0,0 +1,15 @@ +CC=cc +CCOPTS=-Wall -Wpedantic + +all: mandelbrot xmandelbrot + +mandelbrot: mandelbrot.c + $(CC) $(CCOPTS) -o mandelbrot mandelbrot.c -lcurses + +xmandelbrot: xmandelbrot.c + $(CC) $(CCOPTS) -o xmandelbrot xmandelbrot.c -lX11 + +clean: + rm -f *.o + rm -f mandelbrot + rm -f xmandelbrot diff --git a/unix/mandelbrot.c b/unix/mandelbrot.c new file mode 100644 index 0000000..1507f6b --- /dev/null +++ b/unix/mandelbrot.c @@ -0,0 +1,178 @@ +/* + * Mandelbrot for UNIX. + * Copyright 2025, Andrew C. Young + * License: MIT + * + * To compile: cc -o mandelbrot mandelbrot.c -lcurses + * + * This program was written on a Macintosh Quadra 700 running A/UX 3.1. + * A/UX was Apple's first version of UNIX, based on both SYSV and BSD. + * A/UX 3.1 was released in 1994, and its default C compiler did not + * yet support the ANSI standard. As such, this program is written to + * work with earlier K&R versions of C. It should also compile with GCC. + * + * It uses the curses library for screen manipulation, but it should + * also work with the later ncurses library. + * + * My classic Mac resources can be found at m68k.club (www,gopher). + */ + +#include +#include + +/* Used to write to the screen */ +#include + +/* Used to measure run time */ +#include +#include + +#define MAX_ITERATIONS 16 + +int *pixels = 0; +int cols = 80; +int lines = 24; + +char symbols[] = {' ','-','+','=','\\','|','/','*', + '#','3','8','B','=','#','+','.'}; + +char *header = "[ Mandelbrot by Andrew C. Young ]"; +char *footer = "[ Press Any Key to Exit ]"; + +WINDOW *win = 0; + +void init() +{ + initscr(); + nonl(); + cbreak(); + noecho(); + + /* Draw Box */ + wmove(stdscr,0,0); + wclear(stdscr); + box(stdscr,'|','-'); + wmove(stdscr,0,0); + waddch(stdscr, '+'); + wmove(stdscr,0,COLS-1); + waddch(stdscr, '+'); + wmove(stdscr,LINES-1,0); + waddch(stdscr, '+'); + wmove(stdscr,LINES-1,COLS-1); + waddch(stdscr, '+'); + wmove(stdscr,0,( (COLS/2) - (strlen(header)/2) -1 ) ); + waddstr(stdscr, header); + wrefresh(stdscr); + + lines = LINES - 2; + cols = COLS - 2; + win = newwin(lines,cols,1,1); + pixels = (int*)malloc(cols*lines*sizeof(int)); +} + +void cleanup() +{ + if (pixels != 0) + { + free(pixels); + } + endwin(); +} + +void calculate() +{ + int i, col, row, iteration; + double width, height, x0, y0, x, y, xtemp; + + width = (double)cols; + height = (double)lines; + + i = 0; + + for (row = 0; row < lines; row++) + { + y0 = (row * 2.1 / height) - 1; + for (col = 0; col < cols; col++) + { + x0 = (col * 3.5 / width) - 2.5; + x = 0.0; + y = 0.0; + iteration = 0; + while ( ((x*x) + (y*y) <= 4) && (iteration < MAX_ITERATIONS)) + { + xtemp = (x*x) - (y*y) + x0; + y = (2*x*y) + y0; + x = xtemp; + iteration++; + } + + /* printf("%X",iteration); */ + pixels[i] = iteration; + i++; + } + } +} + +void display() +{ + int i, col, row; + int value; + char symbol; + + /* Clear the screen */ + wmove(win,0,0); + wclear(win); + + i = 0; + + for (row = 0; row < lines; row++) + { + wmove(win,row,0); + for (col = 0; col < cols; col++) + { + value = pixels[i]; + value = value % 16; + symbol = symbols[value]; + waddch(win,symbol); + i++; + } + } + wrefresh(win); +} + +void pause() +{ + wmove(stdscr,LINES-1,( (COLS/2) - (strlen(footer)/2) -1 ) ); + waddstr(stdscr, footer); + wmove(stdscr,LINES-1,COLS-1); + wrefresh(stdscr); + getchar(); +} + +double time_in_seconds() +{ + struct tms buffer; + clock_t t = times(&buffer); + return t / 60.0; +} + +int main() +{ + double start, after_calc, after_display, calc_time, display_time; + + init(); + start = time_in_seconds(); + calculate(); + after_calc = time_in_seconds(); + display(); + after_display = time_in_seconds(); + pause(); + cleanup(); + + calc_time = (after_calc - start); + display_time = (after_display - after_calc); + + printf("\nCalculation Time: %0.3f secs, Display Time: %0.3f secs, Total Time: %0.3f secs\n", calc_time, display_time, calc_time + display_time); + + return 0; +} diff --git a/unix/xmandelbrot.c b/unix/xmandelbrot.c new file mode 100644 index 0000000..8761a54 --- /dev/null +++ b/unix/xmandelbrot.c @@ -0,0 +1,417 @@ +/* + * Mandelbrot for X11. + * Copyright 2025, Andrew C. Young + * License: MIT + * + * To compile: cc -o xmandelbrot xmandelbrot.c -lX11 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +char *title = "xmandelbrot"; + +struct color { + unsigned char r, g, b; +}; + +struct color colors_1bit[2]; +struct color colors_2bit[4]; +struct color colors_4bit[16]; +struct color colors_8bit[256]; + +#define DEFAULT_X_SHIFT 2.5 +#define DEFAULT_Y_SHIFT 1.0 +#define DEFAULT_X_SCALE 3.5 +#define DEFAULT_Y_SCALE 2.1 + +struct viewport { + double x_shift, y_shift; + double x_scale, y_scale; +}; + +void get_window_size(display, window, height, width) + Display *display; + Window window; + unsigned int *height; + unsigned int *width; +{ + Window root; + int x, y; + unsigned int border, depth; + XGetGeometry(display, window, &root, &x, &y, width, height, &border, &depth); +} + +unsigned long lookup_color(display, colormap, color) + Display *display; + Colormap colormap; + struct color color; +{ + XColor screen; + + /* Convert our 8-bit color space into a 16-bit color space */ + screen.red = (color.r << 8) | color.r; + screen.green = (color.g << 8) | color.g; + screen.blue = (color.b << 8) | color.b; + + if (!XAllocColor(display, colormap, &screen)) + { + printf("Color Not Found: rgb:%02x/%02x/%02x\n", color.r, color.g, color.b); + return 0; + } + return screen.pixel; +} + +double time_in_seconds() +{ + struct tms buffer; + clock_t t = times(&buffer); + return t / 60.0; +} + +void calculate(display, window, gc, palette, palette_size, viewport) + Display *display; + Window window; + GC gc; + unsigned long int *palette; + size_t palette_size; + struct viewport viewport; +{ + unsigned int i, col, row, iteration; + double width, height, x0, y0, x, y, xtemp; + unsigned int lines, cols; + unsigned long color; + double start, finish; + + start = time_in_seconds(); + + get_window_size(display, window, &lines, &cols); + height = (double)lines; + width = (double)cols; + + XClearWindow(display, window); + + for (row = 0; row < lines; row++) + { + // printf("Row: %u\n", row);q + y0 = (row * viewport.y_scale / height) - viewport.y_shift; + for (col = 0; col < cols; col++) + { + x0 = (col * viewport.x_scale / width) - viewport.x_shift; + x = 0.0; + y = 0.0; + iteration = 0; + while ( ((x*x) + (y*y) <= 4) && (iteration < palette_size) ) + { + xtemp = (x*x) - (y*y) + x0; + y = (2*x*y) + y0; + x = xtemp; + iteration++; + } + + i = (iteration % palette_size); + color = palette[i]; + + XSetForeground(display, gc, color); + XDrawPoint(display, window, gc, col, row); + } + } + + finish = time_in_seconds(); + printf("Height: %u, Width: %u, Time: %0.3f Seconds (Viewport: {x - %0.3f, y - %0.3f, x * %0.3f, y * %0.3f})\n", lines, cols, (finish-start), viewport.x_shift, viewport.y_shift, viewport.x_scale, viewport.y_scale); + +} + +struct color color(r, g, b) + unsigned char r, g, b; +{ + struct color c; + c.r = r; + c.g = g; + c.b = b; + return c; +} + +void initialize_colors() +{ + int steps[16]; + int idx, i, j, v, r, g, b; + + /* Macintosh System 7 1-bit palette (Black, White) */ + colors_1bit[0] = color(0x00, 0x00, 0x00); + colors_1bit[1] = color(0xff, 0xff, 0xff); + + /* Macintosh System 7 2-bit palette */ + colors_2bit[0] = color(0x00, 0x00, 0x00); + colors_2bit[1] = color(0x44, 0x44, 0x44); + colors_2bit[2] = color(0xbb, 0xbb, 0xbb); + colors_2bit[3] = color(0xff, 0xff, 0xff); + + /* Macintosh System 7 4-bit palette (16 colors, XLib rgb format) */ + colors_4bit[0] = color(0x00, 0x00, 0x00); + colors_4bit[1] = color(0x88, 0x00, 0x00); + colors_4bit[2] = color(0x00, 0x88, 0x00); + colors_4bit[3] = color(0x00, 0x00, 0x88); + colors_4bit[4] = color(0x88, 0x88, 0x00); + colors_4bit[5] = color(0x00, 0x88, 0x88); + colors_4bit[6] = color(0x88, 0x00, 0x88); + colors_4bit[7] = color(0x44, 0x44, 0x44); + colors_4bit[8] = color(0xbb, 0xbb, 0xbb); + colors_4bit[9] = color(0xff, 0x88, 0x88); + colors_4bit[10] = color(0x88, 0xff, 0x88); + colors_4bit[11] = color(0x88, 0x88, 0xff); + colors_4bit[12] = color(0xff, 0xff, 0x88); + colors_4bit[13] = color(0x88, 0xff, 0xff); + colors_4bit[14] = color(0xff, 0x88, 0xff); + colors_4bit[15] = color(0xff, 0xff, 0xff); + + /* Macintosh 8-bit system palette (from lospec.com, 256 colors) */ + idx = 0; + + steps[0] = 0x00; + steps[1] = 0x0b; + steps[2] = 0x22; + steps[3] = 0x44; + steps[4] = 0x55; + steps[5] = 0x77; + steps[6] = 0x88; + steps[7] = 0xaa; + steps[8] = 0xbb; + steps[9] = 0xdd; + steps[10] = 0xee; + steps[11] = 0x33; + steps[12] = 0x66; + steps[13] = 0x99; + steps[14] = 0xcc; + steps[15] = 0xff; + + /* grayscale */ + for (i = 0; i < 11; i++) + { + v = steps[i]; + colors_8bit[idx++] = color(v, v, v); + } + + /* 10 blue, 10 green, 10 red */ + for (j = 0; j < 3; j++) + { + for (i = 1; i < 11; i++) + { + v = steps[i]; + switch (j) + { + case 0: colors_8bit[idx++] = color(0x00, 0x00, v); break; /* blue */ + case 1: colors_8bit[idx++] = color(0x00, v, 0x00); break; /* green */ + case 2: colors_8bit[idx++] = color(v, 0x00, 0x00); break; /* red */ + } + } + } + + /* the rest */ + for (r = 10; r < 16; r++) + { + for (g = 10; g < 16; g++) + { + for (b = 10; b < 16; b++) + { + if (r == 10 && g == 10 && b == 10) continue; /* skip black */ + colors_8bit[idx++] = color(steps[r == 10 ? 0 : r], steps[g == 10 ? 0 : g], steps[b == 10 ? 0 : b]); + } + } + } + +} + +void create_palette(display, colormap, palette, colors, size) + Display *display; + Colormap colormap; + unsigned long *palette; + struct color *colors; + size_t size; +{ + size_t i; + for (i = 0; i < size; i++) + { + palette[i] = lookup_color(display, colormap, colors[i]); + } +} + +int main(argc,argv) + int argc; + char **argv; +{ + + /* X11 variables */ + + Display *display; + Window window; + + GC gc; + + XEvent event; + KeySym key; + + XSizeHints hint; + + int screen; + unsigned long fg, bg; + int i; + char text[10]; + int done; + unsigned int width, height, lastWidth, lastHeight; + struct color *colors = colors_8bit; + Colormap colormap; + struct viewport viewport; + int max_iterations = 256; + unsigned long palette[256]; + int x_center, y_center; + double click_x, click_y; + + viewport.x_shift = DEFAULT_X_SHIFT; + viewport.y_shift = DEFAULT_Y_SHIFT; + viewport.x_scale = DEFAULT_X_SCALE; + viewport.y_scale = DEFAULT_Y_SCALE; + + if (argc > 1) + { + max_iterations = atoi(argv[1]); + if (max_iterations < 1 || max_iterations > 256) + { + fprintf(stderr, "Usage: %s [max_iterations (1-256)]\n", argv[0]); + exit(1); + } + } + + initialize_colors(); + + /* setup display/screen */ + display = XOpenDisplay(""); + if (!display) { + fprintf(stderr, "Error: Unable to open display\n"); + exit(1); + } + + screen = DefaultScreen(display); + + colormap = DefaultColormap(display, screen); + + if (max_iterations < 1 || max_iterations > 256) + { + fprintf(stderr, "Error: max_iterations must be between 1 and 256\n"); + exit(1); + } + + if (max_iterations <= 2) + colors = colors_1bit; + else if (max_iterations <= 4) + colors = colors_2bit; + else if (max_iterations <= 16) + colors = colors_4bit; + else + colors = colors_8bit; + + create_palette(display, colormap, palette, colors, max_iterations); + + /* drawing contexts for an window */ + bg = BlackPixel(display, screen); + fg = WhitePixel(display, screen); + hint.x = 100; + hint.y = 100; + hint.width = 500; + hint.height = 300; + hint.flags = PPosition|PSize; + + /* create window */ + window = XCreateSimpleWindow(display, DefaultRootWindow(display), + hint.x, hint.y, + hint.width, hint.height, + 5, fg, bg); + + /* window manager properties (yes, use of StdProp is obsolete) */ + XSetStandardProperties(display, window, title, title, + None, argv, argc, &hint); + + /* graphics context */ + gc = XCreateGC(display, window, 0, 0); + XSetBackground(display, gc, bg); + XSetForeground(display, gc, fg); + + /* allow receiving mouse events */ + XSelectInput(display,window, + ButtonPressMask|KeyPressMask|ExposureMask); + + /* show up window */ + XMapRaised(display, window); + + /* event loop */ + done = 0; + while(done==0){ + + /* fetch event */ + XNextEvent(display, &event); + + switch(event.type){ + case Expose: + /* Window was shown. */ + if(event.xexpose.count == 0) + { + get_window_size(display, window, &height, &width); + if (width != lastWidth || height != lastHeight) + { + calculate(display, window, gc, palette, max_iterations, viewport); + } + lastWidth = width; + lastHeight = height; + } + break; + case MappingNotify: + /* Modifier key was up/down. */ + XRefreshKeyboardMapping(&event.xmapping); + break; + case ButtonPress: + /* Mouse button was pressed. */ + /* Calculate the world coordinates of the clicked point */ + click_x = (event.xbutton.x * viewport.x_scale / width) - viewport.x_shift; + click_y = (event.xbutton.y * viewport.y_scale / height) - viewport.y_shift; + + /* increase or decrease the scale of the viewport depending on the button pressed */ + if (event.xbutton.button == 1) + { + viewport.x_scale *= 0.75; + viewport.y_scale *= 0.75; + } + else if (event.xbutton.button == 3) + { + viewport.x_scale *= 1.25; + viewport.y_scale *= 1.25; + } + + /* Adjust the viewport to center on the clicked point */ + x_center = width / 2; + y_center = height / 2; + viewport.x_shift = (x_center * viewport.x_scale / width) - click_x; + viewport.y_shift = (y_center * viewport.y_scale / height) - click_y; + + calculate(display, window, gc, palette, max_iterations, viewport); + break; + case KeyPress: + /* Key input. */ + i = XLookupString(&event.xkey, text, 10, &key, 0); + if(i==1 && text[0]=='q') done = 1; + break; + } + } + + /* finalization */ + XFreeGC(display,gc); + XDestroyWindow(display, window); + XCloseDisplay(display); + + exit(0); +}