/* * K A L E I D * * Copyright (C) 1981, 1984 * Martin Minow * Arlington, MA * * In the screen output routines, row 1 is the top row, row 0 means * "don't move the cursor", Col 1 is left-margin; col 0 is meaningless. * vtout(row, col, NULL) flushes the buffer. * * NOTE: You must set your terminal to "NOWRAP" */ /*)BUILD */ #include #define EOS 0 /* End of string */ #ifdef rt11 int $$narg = 1; /* Don't argv prompt */ #endif extern int isvt52; /* TRUE if vt52 output */ char inline[513]; /* Argument line */ /* * Note: inline must be dimensioned longer than the longest possible * output string (20 * "longest move + 1") + 1 */ /* * The font.. strings select the characters written into the various * quadrants: ul = upper left, etc. */ char fontul[] = "!#@$%:-+|=**..~ >< )( ][ `' \\/ "; char fontur[] = "!#@$%:-+|=**..~ <> () [] '` /\\ "; char fontll[] = "!#@$%:-+|=**..~ >< )( ][ '` /\\ "; char fontlr[] = "!#@$%:-+|=**..~ <> () [] `' \\/ "; #define FONTSIZE ((sizeof fontul) - 1) int verbose = FALSE; /* * Define the kaleidiscope as a circle centered on the display screen. * The screen is 24 lines high. Thus the vertical radius is 12. * For an aspect ratio (width:height) of 0.6, the corresponding * horizontal radius, MAXX, will be 21. * * ytab[MAXX] entries give the maximum height for a given horizontal * distance from the center. Values were tuned by inspection. */ #define MAXX 21 /* Max. horizontal distance */ /* * Y-axis table. Change this to change the shape of the display. */ int ytab[MAXX] = { 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 4, 1, }; main(argc, argv) register int argc; register char **argv; { register int count; #ifndef decus extern char *getenv(); #endif /* * Determine the terminal type and initialize the screen handler */ #ifndef decus isvt52 = (strcmp(getenv("TERM"), "vt52") == 0 || strcmp(getenv("TERM"), "VT52") == 0); #endif if (argc > 1 && (strcmp(argv[1], "-vt52") == 0 || strcmp(argv[1] == "-VT52") == 0)) { isvt52 = TRUE; } erpage(1, 1); /* Erase entire page */ for (;;) kaleid(); } kaleid() /* * Do one kaleidiscope vector */ { register int x1; register int y1; register int x2; register int y2; int whichc; extern int irand(); /* Returns rand() mod argument */ do { /* * Get the starting and ending points of the vector */ x1 = irand(MAXX); if ((y1 = ytab[x1]) != 0) y1 = irand(y1); x2 = irand(MAXX); if ((y2 = ytab[x2]) != 0) y2 = irand(y2); } while (x1 == x2 && y1 == y2); /* * Plot the kaleidiscope pattern. * Note that [x1,y1] and [x2,y2] are * in the upper-left quadrant. * Plot calls xymove with values for all * four quadrants. */ whichc = irand(FONTSIZE); if (verbose) { sprintf(inline, "[%2d, %2d]\r\n[%2d, %2d] %2d", x1, y1, x2, y2, whichc); vtout(1, 1, inline); vtout(0, 0, NULL); } xymove(40 + x1, 12 - y1, 40 + x2, 12 - y2, fontur[whichc]); xymove(40 + x1, 13 + y1, 40 + x2, 13 + y2, fontlr[whichc]); xymove(39 - x1, 12 - y1, 39 - x2, 12 - y2, fontul[whichc]); xymove(39 - x1, 13 + y1, 39 - x2, 13 + y2, fontll[whichc]); } /* * There are two move strings per octant -- one for straight-line * movement, one for diagonals. The octant numbering is defined * by a series of tests. The numbering forms a gray code: * * -X +X * * \ 1 | 5 / abs(Y-difference) > abs(X-difference) * -Y 0 \ | / 4 abs(Y-difference) < abs(X-difference) * \ | / * ------+------ * / | \ * +Y 2 / | \ 6 abs(Y-difference) < abs(X-difference) * / 3 | 7 \ abs(Y-difference) > abs(X-difference) * * In the following, \233 is a "parity escape." By setting the * parity bit, (some) operating systems will not notice the * control character. The vt100 sequences follow the ANSI standard * for display control. */ typedef struct move { char *straight; char *diagonal; } MOVE; MOVE vt52move[] = { { "\b\b", "\b\b\233A" }, /* -X -X -Y 0 */ { "\b\233A", "\b\b\233A" }, /* -Y -X -Y 1 */ { "\b\b", "\b\b\233B" }, /* -X -X +Y 2 */ { "\b\233B", "\b\b\233B" }, /* +Y -X +Y 3 */ { "", "\233A" }, /* +X +X -Y 4 */ { "\b\233A", "\233A" }, /* -Y +X -Y 5 */ { "", "\233B" }, /* +X +X +Y 6 */ { "\b\233B", "\233B" }, /* +Y +X +Y 7 */ }; MOVE vt100move[] = { { "\b\b", "\b\b\233[A" }, /* -X -X -Y 0 */ { "\b\233[A", "\b\b\233[A" }, /* -Y -X -Y 1 */ { "\b\b", "\b\b\233[B" }, /* -X -X +Y 2 */ { "\b\233[B", "\b\b\233[B" }, /* +Y -X +Y 3 */ { "", "\233[A" }, /* +X +X -Y 4 */ { "\b\233[A", "\233[A" }, /* -Y +X -Y 5 */ { "", "\233[B" }, /* +X +X +Y 6 */ { "\b\233[B", "\233[B" }, /* +Y +X +Y 7 */ }; xymove(x1, y1, x2, y2, c) int x1, y1; /* From here */ int x2, y2; /* To here */ char c; /* Spray me */ /* * Stockton's algorithm from CACM (Algorithm 162) -- as revised * (reinvented) by Bresenham (IBM Systems Journal vol. 4, no. 1, 1965). * This implementation is based on one by Don North and closely follows * Bresenham's version. */ { int dx, dy; /* X and Y distance */ int smagic; /* Magic straight move increment */ int dmagic; /* Magic diagonal move increment */ register int longer; /* Long side of the plot triangle */ register int dda; /* Distance to hypotenuse (diagonal) */ char *smove; /* -> Straight line move string */ char *dmove; /* -> Diagonal line move string */ register union { char *tp; /* Text pointer */ MOVE *index; /* Octant index */ } r; extern char *cpystr(); /* Copy string, return ptr. to end */ /* * Determine which octant. */ r.index = (isvt52) ? &vt52move[0] : &vt100move[0]; if ((dx = x2 - x1) >= 0) r.index += 4; if ((dy = y2 - y1) >= 0) r.index += 2; /* * Determine the move parameters: * r.index The octant in which the move takes place. * longer The longer of the two sides of the right triangle * [x1,y1], [x1, y2], [x2, y2]. The computation * calculation uses absolute distances, normalizing * the triangle to the upper right quadrant. * shorter The other side (not explicitly calculated). * smagic A magic number for straight moves: (shorter * 2) * dmagic A magic number for diagonal moves: * (2 * (smagic - longer)) * dda The distance to the diagonal. The intial value * is: ((2 * smagic) - shorter). * smove -> the string for straight moves. * dmove -> the string for diagonal moves. * The magic numbers are explained (somewhat) in Bresenham. * Stockton's algorithm does the same thing in an even less * transparent manner. */ if ((dx = abs(dx)) >= (dy = abs(dy))) { longer = dx; /* dx is the long side */ smagic = dy * 2; } else { longer = dy; /* dy is the long side */ smagic = dx * 2; r.index++; /* Finialize the octant */ } dda = smagic - longer; /* Initial error value */ dmagic = dda - longer; /* Diagonal magic value */ smove = r.index->straight; /* Output smove on a straight */ dmove = r.index->diagonal; /* and dmove on a diagonal move */ /* * Do the pen moves. */ r.tp = inline; /* Store pen moves into inline */ while (*r.tp++ = c, --longer >= 0) { if (dda < 0) { r.tp = cpystr(r.tp, smove); /* Straight */ dda += smagic; } else { r.tp = cpystr(r.tp, dmove); /* Diagonal */ dda += dmagic; } } *r.tp = EOS; /* Terminate pen move string */ vtout(y1, x1, inline); /* and write it to the screen */ } /* * Random stuff */ #ifndef decus int irand(max) register int max; /* * Return a value from 0 to max-1. */ { register long temp; temp = (rand() & 32767L) * max; return (temp / 32768L); } char * cpystr(out, in) register char *out, *in; /* * Copy in to out, return a pointer to the trailing EOS. */ { while ((*out++ = *in++) != EOS) ; return (out - 1); } #endif /* * S i m p l e S c r e e n F u n c t i o n s * * All functions work in terms of [row, column] where the upper-left * corner is [1, 1]. Note that, in all routines, if row is zero, * the cursor will not be moved before the operation commenses. * * Set global isvt52 non-zero if running on that vernerable beast. * * You may need to call vtout(0, 0, NULL) occasionally. */ int isvt52 = FALSE; #ifdef decus extern int $$rsts; /* On RSTS/E, we must set ESC's 8th bit */ #endif vtcurse(r, c) int r, c; /* * Move the cursor to [r,c]. r == 0 is a no-op. */ { if (r == 0) return; #ifdef decus if (isvt52) { printf(($$rsts) ? "\r\233Y%c%c" : "\r\033Y%c%c", r + 040 - 1, c + 040 - 1); } else { printf(($$rsts) ? "\233[%d;%dH" : "\033[%d;%dH", r, c); } #else if (isvt52) { printf("\r\033Y%c%c", r + 040 - 1, c + 040 - 1); } else { printf("\033[%d;%dH", r, c); } #endif } erpage(r, c) int r, c; /* * Erase the page from [r,c]. If r == 0, erase from current position. */ { vtcurse(r, c); #ifdef decus if (isvt52) fputs(($$rsts) ? "\233J" : "\033J", stdout); else fputs(($$rsts) ? "\233[J" : "\033[J", stdout); #else if (isvt52) fputs("\033J", stdout); else fputs("\033[J", stdout); #endif } erline(r, c) int r, c; /* * Erase to end of line from [r,c]. If r == 0, don't move cursor. */ { vtcurse(r, c); #ifdef decus if (isvt52) fputs(($$rsts) ? "\233K" : "\033K", stdout); else fputs(($$rsts) ? "\233[K" : "\033[K", stdout); #else if (isvt52) fputs("\033K", stdout); else fputs("\033[K", stdout); #endif } vtout(r, c, text) int r, c; char *text; /* * Write text starting at [r, c]. If r == 0, don't move cursor. */ { vtcurse(r, c); fputs(text, stdout); }