/* * K A L E I D * * Copyright (C) 1981 * Martin Minow * Arlington, MA * * Note: these routines use a general purpose display screen handler for * vt52/vt100's: * sctype() returns terminal type * scset() setup buffered mode * scget() prompt, flush buffer, and read 1 line of text * scerpg() erase page from (row, column) * scerln() erase line from (row, column) * scout() output text from (row, column) * scput() exit buffered mode. * * Where row 1 is top of screen and row 0 means "don't move cursor." * Col 1 is left-margin; col 0 is meaningless (unless row == 0). * In scout(row, col, text), text == "" means just move cursor, * text == NULL means flush buffered text. */ /*)BUILD */ #include #define EOS 0 /* End of string */ #ifdef rt11 int $$narg = 1; /* Don't argv prompt */ #endif char inline[513]; /* Argument line */ /* * Note: inline must be dimensioned longer than the longest possible * output string (20 * "longest move + 1") + 1 */ #define VT52 (1+64) /* From sctype */ #define VT100 (1+96) /* From sctype */ int tty_type; /* What sctype says it is */ char **oldbuf; /* For screen package */ char buffer[800]; /* For screen package */ /* * 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; #ifdef rt11 extern int $$rsts; extern int exit(); #endif /* * Determine the terminal type and initialize the screen handler */ if ((tty_type = sctype()) != VT52 && tty_type != VT100) error("Sorry, kaleid requires a VT52 or VT100 terminal"); scset(buffer, sizeof buffer, &oldbuf); #ifdef rt11 if ($$rsts) setcc(&exit); /* Trap Exit interrupt */ #endif scerpg(1, 1); for (;;) { kaleid(); } } windup() /* * Called by the system on exit (or CTRL/C on RSTS/RT11) */ { if (oldbuf != NULL) { scerpg(1, 1); scout(0, 0, NULL); scput(oldbuf); oldbuf = NULL; } } 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); scout(1, 1, inline); scout(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\n" }, /* -X -X +Y 2 */ { "\b\n", "\b\b\n" }, /* +Y -X +Y 3 */ { "", "\233A" }, /* +X +X -Y 4 */ { "\b\233A", "\233A" }, /* -Y +X -Y 5 */ { "", "\n" }, /* +X +X +Y 6 */ { "\b\n", "\n" }, /* +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\n" }, /* -X -X +Y 2 */ { "\b\n", "\b\b\n" }, /* +Y -X +Y 3 */ { "", "\233[A" }, /* +X +X -Y 4 */ { "\b\233[A", "\233[A" }, /* -Y +X -Y 5 */ { "", "\n" }, /* +X +X +Y 6 */ { "\b\n", "\n" }, /* +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 = (tty_type == VT52) ? &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 */ scout(y1, x1, inline); /* and write it to the screen */ }