dos_compilers/Microsoft C v5/SRC/GRDEMO.C
2024-07-04 11:24:57 -07:00

925 lines
20 KiB
C
Raw Blame History

/*
GRDEMO.C - Demonstrates capabilities of the QuickC graphics library
Since this program uses functions not found in the QuickC core
library or the graphics library, you must use a program list to
compile in memory. Select Set Program List from the File menu
and enter GRDEMO as the program list name. Then specify GRDEMO
as the only entry in the program list.
An alternative is to create a quick library containing the routines
used in GRDEMO. Use the following lines to build the quick library
and load it into QuickC:
QLIB /s grdemo.c
QC /l grdemo.qlb grdemo
*/
#include <graph.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <bios.h>
/* Functions - prototypes and macros */
/* Demo functions */
int main(void);
/* Demo selections */
void circles(void);
void sphere(void);
int polygons(void);
int spiral(int angle, int inc);
int inspiral(int side, int angle, int inc);
void adjust(void);
/* Menus */
int menu(int row, int col, char * *items);
void box(int row, int col, int hi, int wid);
void itemize(int row, int col, char *str, int len);
/* Color changes */
int nextatrib(int init);
void nextcolor(void);
/* Turtle graphics routines */
/* Initiate and set defaults */
short setturtle(short mode);
void home(void);
/* Control pen and color */
int pendown(int state);
short fillstate(short state);
short penatrib(short atrib);
short borderatrib(short border);
/* Control angle */
short turn(short angle);
short turnto(short angle);
/* Turtle movement */
short move(short distance);
short moveto(short x,short y);
short poly(short number,short side);
/* Figures (defined as macros) */
/* Puts a circle with radius <r> at current location.
Returns nonzero if successful. */
#define circle(r) _ellipse(tc.isFill, \
tc.xCur-(r), adj(tc.yCur-(r)), tc.xCur+(r), adj(tc.yCur+(r)))
/* Puts an ellipse with width <w> and height <h> at current location.
Returns nonzero if successful. */
#define ellipse(w,h) _ellipse(tc.isFill, tc.xCur-((w)/2), \
adj(tc.yCur-((h)/2)), tc.xCur+((w)/2), adj(tc.yCur+((h)/2)))
/* Miscellaneous */
/* Fills starting at the current location.
Returns nonzero if successful. */
#define fillin() _floodfill(tc.xCur, adj(tc.yCur),tc.border)
/* Returns nonzero if the current location is onscreen. */
#define onscreen() (!((tc.xCur < -xMAX) || (tc.xCur > xMAX) || \
(tc.yCur < -yMAX) || (tc.yCur > yMAX)))
/* Returns a long int mixed from red <r>, green <g>, and blue <b> bytes. */
#define RGB(r,g,b) (((long) ((b) << 8 | (g)) << 8) | (r))
short adj(short y);
unsigned cursor(unsigned value);
/* Constants */
#define xMAX (tc.xCnt / 2)
#define yMAX (tc.yCnt / 2)
#define CIRCUMFERENCE 360
#define HALFCIRCUMFERENCE 180
#define PI 3.141593
#define DEFAULT -1
#define TRUE 1
#define FALSE 0
#define TCURSOROFF 0x2020
#define LASTATR 15
#define NLASTATR 14
/* Scan codes */
#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ENTER 28
/* Structures for configuration and other data */
struct videoconfig vc;
struct {
long xUnits, yUnits;
short xCnt, yCnt;
short xCur, yCur;
short angle;
short border;
short numAtribs;
short numColors;
short atrib;
short isPen;
short isFill;
} tc = { 125L, 90L }; /* Initial values for aspect.
Change to adjust for your screen. */
/* Initial and work arrays for remapable colors. */
long iColors[] = {
_BLACK, _BLUE, _GREEN, _CYAN,
_RED, _MAGENTA, _BROWN, _WHITE,
_GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN,
_LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE };
long wColors[256];
/* Array and enum for main menu */
char *mnuMain[] = {
"Quit",
"Circles",
"Sphere",
"Tunnel",
"Spiral",
"Inverted Spiral",
"Adjust Aspect",
"Change Mode",
NULL };
/* Define constants 0 to 7 (equivalent to multiple #define statements) */
enum choice { QUIT, CIRCLES, SPHERE, TUNNEL, SPIRAL,
INSPIRAL, ADJUST, CHANGE };
/* Arrays for video modes */
char *mnuModes[] = {
"MRES4COLOR ",
"MRESNOCOLOR",
"HRESBW",
"MRES16COLOR",
"HRES16COLOR",
"ERESCOLOR",
"VRES2COLOR",
"VRES16COLOR",
"MRES256COLOR",
NULL };
/* Array for modes */
int modes[] = {
_MRES4COLOR,
_MRESNOCOLOR,
_HRESBW,
_MRES16COLOR,
_HRES16COLOR,
_ERESCOLOR,
_VRES2COLOR,
_VRES16COLOR,
_MRES256COLOR,
_ERESNOCOLOR,
_HERCMONO,
DEFAULT };
/* Structure for menu attributes (variables for color and monochrome) */
struct mnuAtr {
int fgNormal, fgSelect, fgBorder;
long bgNormal, bgSelect, bgBorder;
int centered;
char nw[2], ne[2], se[2], sw[2], ns[2], ew[2];
};
struct mnuAtr menus = {
0x00, 0x0f, 0x04,
0x03, 0x04, 0x03,
TRUE,
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>"
};
struct mnuAtr bwmenus = {
0x07, 0x00, 0x07,
0x00, 0x07, 0x00,
TRUE,
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>"
};
char mess1[] = { "Graphics Demonstration Program" } ;
char mess2[] = { "Move to menu selection with cursor keys" };
char mess3[] = { "Press ENTER to select" };
int lmess1 = sizeof(mess1), lmess2 = sizeof(mess2), lmess3 = sizeof(mess3);
main ()
{
int choice, crow, ccol;
int vmode;
_setvideomode (_DEFAULTMODE);
_getvideoconfig(&vc);
crow = vc.numtextrows / 2;
ccol = vc.numtextcols / 2;
/* Select best text and graphics modes and adjust menus */
switch (vc.adapter) {
case _MDPA :
puts("No graphics mode available.\n");
exit(0);
case _CGA :
mnuModes[3] = NULL;
vmode = _MRES4COLOR;
break;
case _HGC :
mnuModes[6] = NULL;
vmode = _HERCMONO;
break;
case _EGA :
mnuModes[6] = NULL;
if (vc.memory > 64)
vmode = _ERESCOLOR;
else
vmode = _HRES16COLOR;
break;
case _VGA :
case _MCGA :
vmode = _MRES256COLOR;
break;
}
switch (vc.mode) {
case _TEXTBW80 :
case _TEXTBW40 :
menus = bwmenus;
break;
case _TEXTMONO :
case _ERESNOCOLOR :
case _HERCMONO :
menus = bwmenus;
if (vmode != _HERCMONO)
vmode = _ERESNOCOLOR;
mnuMain[7] = NULL;
}
srand((unsigned)time(0L));
_settextposition(1,ccol - (lmess1 / 2));
_outtext(mess1);
_settextposition(2,ccol - (lmess2 / 2));
_outtext(mess2);
_settextposition(3,ccol - (lmess3 / 2));
_outtext(mess3);
/* Select and branch to menu choices */
for (;;) {
choice = menu(crow,ccol,mnuMain);
setturtle(vmode);
switch (choice) {
case QUIT :
_setvideomode (_DEFAULTMODE); exit(0);
case CIRCLES :
circles(); break;
case SPHERE :
sphere(); break;
case TUNNEL :
nextatrib(0);
pendown(FALSE);
moveto((short)(-xMAX * .3), (short)(yMAX * .3));
turn(35);
pendown(TRUE);
polygons();
while (!kbhit())
nextcolor();
break;
case SPIRAL :
nextatrib(0);
spiral((rand() % 50) + 30, (rand() % 4) + 1);
while (!kbhit())
nextcolor();
break;
case INSPIRAL :
nextatrib(0);
inspiral((rand() % 12) + 8, (rand() %20) + 2,
(rand() % 30) + 1);
while (!kbhit())
nextcolor();
break;
case ADJUST :
adjust();
_setvideomode(_DEFAULTMODE);
continue;
case CHANGE :
for (;;) {
_setvideomode (_DEFAULTMODE);
vmode = modes[menu(crow,ccol,mnuModes)];
if (!setturtle(vmode)) {
_settextposition(1,22);
_outtext("Mode not available on this machine.\n");
} else
break;
}
_setvideomode (_DEFAULTMODE);
continue;
}
_bios_keybrd(_KEYBRD_READ);
_setvideomode (_DEFAULTMODE);
}
return (0);
}
/* Put circles of varying sizes and colors on screen in a round pattern */
void circles()
{
int i, x, y;
long tb;
tb = _getbkcolor();
if ((vc.mode < _HRESBW) || (tc.numAtribs == 2))
fillstate(FALSE);
else {
fillstate(TRUE);
_setbkcolor(_BRIGHTWHITE);
}
pendown(FALSE);
for (;;) {
if (tc.numAtribs <= 4)
nextcolor();
for (i = 5; i <= 150; ++i) { /* Draw circles */
x = (int)((xMAX-30) * atan(sin(i / PI)));
y = (int)((yMAX-30) * atan(cos(i / PI)));
penatrib(nextatrib(DEFAULT));
moveto(x,y);
circle (i % 30);
if (kbhit()) {
_setbkcolor(tb);
pendown(TRUE);
return;
}
}
}
}
/* Draw and fill sphere; rotate colors in EGA modes;
change palette in CGA modes */
void sphere()
{
int ix, x, y, border, inc;
y = x = (int)(tc.yCnt * 0.9);
fillstate(FALSE);
nextatrib(0);
inc = y / 14;
border = penatrib(DEFAULT);
borderatrib(border);
for (ix = inc; ix <= x; ix += (inc * 2)) /* Draw circles */
ellipse(ix, y);
fillstate(TRUE);
pendown(FALSE);
turn(90);
x /= 2;
moveto(-x + inc,0);
while (tc.xCur <= (x - inc)) { /* Fill circles */
penatrib(nextatrib(DEFAULT));
fillin();
move(inc);
}
while (!kbhit()) /* Rotate colors */
nextcolor();
pendown(TRUE);
}
/* Draw polygons of increasing size by incrementing the number of sides.
Return 1 for user interrupt, 0 for edge of screen encountered. */
int polygons()
{
int sides = 3, size = 2, atrib = 1;
for (;;) {
penatrib(nextatrib(atrib++ % NLASTATR));
if (!poly(sides++,size++))
return(0);
if (kbhit())
return(1);
}
}
/* Draw a spiral by increasing each side of a rotating figure.
<angle> determines tightness.
<inc> determines size.
Return 1 for user interrupt, 0 for edge of screen encountered. */
int spiral(int angle, int inc)
{
int side = 1, atrib = 1;
for (;;) {
penatrib(nextatrib(atrib++ % NLASTATR));
if (!move(side += inc))
return(0);
turn(angle);
if (kbhit())
return(1);
}
}
/* Draw an inverted spiral by increasing each angle of a rotating figure.
<side> determines size.
<angle> determines shape.
<inc> determines tightness and shape.
Return 1 for user interrupt, 0 for edge of screen encountered. */
int inspiral(int side, int angle, int inc)
{
int atrib = 1;
for (;;) {
penatrib(nextatrib(atrib++ % NLASTATR));
if (!move(side))
return(0);
turn(angle += inc);
if (kbhit())
return(1);
}
}
/* Draw an adjustable circle to enable the user to visually adjust the
screen aspect. */
void adjust()
{
int i, y, pen, radius = (int)(yMAX * .6);
char buffer[3];
pen = penatrib(DEFAULT);
_outtext("Modify initial structure\nvalues in program");
_outtext("\n\nAdjust with cursor keys: ");
_outtext("\n UP - adjust up");
_outtext("\n DOWN - adjust down");
_outtext("\n ENTER - finished\n\n");
_outtext("xUnits 125\n");
for (;;) {
y = (int)tc.yUnits;
penatrib(pen);
home();
pendown(FALSE); /* Draw white circle with cross. */
moveto(75,0);
circle(radius);
for (i = 1; i <= 4; i++) {
pendown(TRUE);
move(radius);
turn(180);
pendown(FALSE);
move(radius);
turn(90);
}
_settextposition(11,1); /* Show units and adjust */
_outtext("yUnits ");
ltoa(tc.yUnits,buffer,10);
_outtext(buffer);
_outtext(" ");
switch ((_bios_keybrd(_KEYBRD_READ) & 0xff00) >> 8) {
case UP :
--y;
break;
case DOWN :
++y;
break;
case ENTER :
tc.yUnits = y;
return;
}
penatrib(0); /* Erase circle with black */
moveto(75,0);
circle(radius);
for (i = 1; i <= 4; i++) {
pendown(TRUE);
move(radius);
turn(180);
pendown(FALSE);
move(radius);
turn(90);
}
tc.yUnits = y;
}
}
/* Put menu on screen.
Starting <row> and <column>.
Array of menu <items> strings.
Global structure variable <menus> determines:
Colors of border, normal items, and selected item.
Centered or left justfied.
Border characters.
Returns number of item selected. */
int menu(int row, int col, char * *items)
{
int i, num, max = 2, prev, curr = 0;
int litem[25];
long bcolor;
cursor(TCURSOROFF);
bcolor = _getbkcolor();
/* Count items, find longest, and put length of each in array */
for (num = 0; items[num]; num++) {
litem[num] = strlen(items[num]);
max = (litem[num] > max) ? litem[num] : max;
}
max += 2;
if (menus.centered) {
row -= num / 2;
col -= max / 2;
}
/* Draw menu box */
_settextcolor(menus.fgBorder);
_setbkcolor(menus.bgBorder);
box(row++,col++,num,max);
/* Put items in menu */
for (i = 0; i < num; ++i) {
if (i == curr) {
_settextcolor(menus.fgSelect);
_setbkcolor(menus.bgSelect);
} else {
_settextcolor(menus.fgNormal);
_setbkcolor(menus.bgNormal);
}
itemize(row+i,col,items[i],max - litem[i]);
}
/* Get selection */
for (;;) {
switch ((_bios_keybrd(_KEYBRD_READ) & 0xff00) >> 8) {
case UP :
prev = curr;
curr = (curr > 0) ? (--curr % num) : num-1;
break;
case DOWN :
prev = curr;
curr = (curr < num) ? (++curr % num) : 0;
break;
case ENTER :
_setbkcolor(bcolor);
return(curr);
default :
continue;
}
_settextcolor(menus.fgSelect);
_setbkcolor(menus.bgSelect);
itemize(row+curr,col,items[curr],max - litem[curr]);
_settextcolor(menus.fgNormal);
_setbkcolor(menus.bgNormal);
itemize(row+prev,col,items[prev],max - litem[prev]);
}
}
/* Draw menu box.
<row> and <col> are upper left of box.
<hi> and <wid> are height and width. */
void box(int row, int col, int hi, int wid)
{
int i;
char temp[80];
_settextposition(row,col);
temp[0] = *menus.nw;
memset(temp+1,*menus.ew,wid);
temp[wid+1] = *menus.ne;
temp[wid+2] = 0;
_outtext(temp);
for (i = 1; i <= hi; ++i) {
_settextposition(row+i,col);
_outtext(menus.ns);
_settextposition(row+i,col+wid+1);
_outtext(menus.ns);
}
_settextposition(row+hi+1,col);
temp[0] = *menus.sw;
memset(temp+1,*menus.ew,wid);
temp[wid+1] = *menus.se;
temp[wid+2] = 0;
_outtext(temp);
}
/* Put an item in menu.
<row> and <col> are left position.
<str> is the string item.
<len> is the number of blanks to fill. */
void itemize(int row, int col, char *str, int len)
{
char temp[80];
_settextposition(row,col);
_outtext(" ");
_outtext(str);
memset(temp,' ',len--);
temp[len] = 0;
_outtext(temp);
}
/* Rotate to next color attribute.
<init> initializes new starting color.
(specify DEFAULT to rotate to next color)
Return rotated color attribute. */
int nextatrib(int init)
{
static int atr;
if (tc.numAtribs == 2)
return(atr = !atr);
if (!(init == DEFAULT))
atr = init;
return(atr = (atr % (tc.numAtribs-1) + 1));
}
/* Rotate to next palette array for EGA and higher.
Rotate palette for CGA color. */
void nextcolor()
{
static int co = 0;
int w, i;
if ((vc.adapter <= _CGA) || !tc.numColors)
return;
if ((--co < 0) || (co >= tc.numColors - 1))
co = tc.numColors - 1;
w = co;
for (i = LASTATR-1; i > 0; --i) {
_remappalette(i, wColors[w]);
if (--w < 0)
w = tc.numColors - 1;
}
}
/* Set the display mode and establish turtle defaults.
<mode> is the mode to set.
Returns 0 if mode is invalid, else returns nonzero. */
short setturtle(short mode)
{
int ret, i = 0, btm, top = 63, inc, red = 0, green = 0, blue = 0;
_getvideoconfig(&vc);
if (mode < _MRES4COLOR)
return(0);
if (!(mode == vc.mode)) {
if(!(ret = _setvideomode(mode)))
return(0);
_getvideoconfig(&vc);
} else
ret = mode;
home();
switch (vc.mode) { /* Set palette defaults */
case _ERESNOCOLOR :
case _HERCMONO :
tc.numColors = 0;
tc.numAtribs = 2;
return(ret);
case _MRES256COLOR :
tc.numColors = tc.numAtribs = 125;
inc = btm = 12;
break;
case _ERESCOLOR :
case _VRES16COLOR :
/* For full 64 color palette, btm = 0; tc.numColors = 64 */
inc = btm = 16; tc.numColors = 27;
break;
case _MRES4COLOR :
case _MRESNOCOLOR :
inc = 32; btm = 0; tc.numColors = 8;
break;
default:
tc.numColors = 16;
memcpy(wColors,iColors,16 * sizeof(iColors[0]));
_remapallpalette(iColors);
nextcolor();
return(ret);
}
/* Fill palette arrays */
for (blue = btm; blue <= top; blue += inc)
for (green = btm; green <= top; green += inc)
for (red = btm; red <= top; red += inc)
wColors[i++] = RGB(red, green, blue);
nextcolor();
return(ret);
}
/* Sets initial turtle parameters. Use to reset without changing mode. */
void home()
{
float ratio;
if (vc.mode == _MRES256COLOR)
tc.numAtribs = 125;
else
tc.numAtribs = vc.numcolors;
tc.xCnt = vc.numxpixels;
tc.yCnt = vc.numypixels;
_setlogorg(tc.xCnt / 2, tc.yCnt / 2);
ratio = (float)(tc.xUnits * tc.yCnt) / (tc.yUnits * tc.xCnt);
tc.yCnt /= ratio;
tc.xCur = 0;
tc.yCur = 0;
tc.isPen = 1;
_moveto(0, 0);
tc.angle = 0;
_remappalette(LASTATR,_BRIGHTWHITE);
borderatrib(LASTATR);
penatrib(LASTATR);
fillstate(_GBORDER);
}
/* Makes the turtle pen used in move() and moveto() visible or invisible.
<state> can be TRUE (visible), FALSE (invisible),
or DEFAULT (return current)
Returns current state. */
int pendown(int state)
{
switch (state) {
case TRUE:
tc.isPen = TRUE;
break;
case FALSE:
tc.isPen = FALSE;
}
return(tc.isPen);
}
/* Determines whether figures should be filled.
<state> can be TRUE (fill), FALSE (border only),
or DEFAULT (return current)
Returns current state. */
short fillstate(short state)
{
switch (state) {
case _GBORDER:
case FALSE:
tc.isFill = _GBORDER;
break;
case _GFILLINTERIOR:
case TRUE:
tc.isFill = _GFILLINTERIOR;
}
return(tc.isFill);
}
/* Sets the color attribute of the pen.
<atrib> is new atribute (use DEFAULT to get current).
Returns current color attribute. */
short penatrib(short atrib)
{
if (!(atrib == DEFAULT)) {
_setcolor(tc.atrib = atrib);
}
return(tc.atrib);
}
/* Sets the color attribute to be used as a boundary in fills.
<border> is new border (use DEFAULT to get current).
Returns border attribute. */
short borderatrib(short border)
{
if (!(border == DEFAULT))
tc.border = border;
return(tc.border);
}
/* Sets a new turtle <angle> relative to the current angle.
Returns the new angle. */
short turn(short angle)
{
return(tc.angle = ((tc.angle + angle) % CIRCUMFERENCE));
}
/* Sets a specified turtle <angle>.
Returns the new angle. */
short turnto(short angle)
{
return(tc.angle = (angle % CIRCUMFERENCE));
}
/* Moves from the current position in the current direction. A line is
drawn if the pen is down. The current position is reset.
<distance> is the adjusted length of line.
Returns 0 if the new position is off the screen. */
short move(short distance)
{
short dX, dY;
double workangle;
workangle = (tc.angle - 90) * PI / HALFCIRCUMFERENCE;
dX = (short)(distance * cos(workangle));
dY = (short)(distance * sin(workangle));
if (tc.isPen)
_lineto(tc.xCur + dX, adj(tc.yCur + dY));
else
_moveto(tc.xCur + dX, adj(tc.yCur + dY));
tc.xCur += dX;
tc.yCur += dY;
return(onscreen());
}
/* Moves from the current position to a specified position. A line is
drawn if the pen is down. The current position is reset.
<x> and <y> are destination coordinates.
Returns 0 if new position is off screen. */
short moveto(short x, short y)
{
if (tc.isPen)
_lineto(x, adj(y));
else
_moveto(x, adj(y));
tc.xCur = x;
tc.yCur = y;
return(onscreen());
}
/* Draws a polygon
<number> specifies how many sides.
<side> specifies the length of each side.
Returns 0 if any part of polygon is off screen. */
short poly(short number, short side)
{
int i, ret = 1;
double angle;
angle = (double)(360 / number);
for (i = 1; i <= number; ++i) {
ret = move(side) && ret;
turn((short)angle);
}
return(ret);
}
/* Adjusts a specified <y> value for screen aspect.
Returns the new value. */
short adj(short y)
{
if (y)
y = (short)(((long)y * (tc.xUnits*vc.numypixels)) /
(tc.yUnits*vc.numxpixels));
return(y);
}
/* Change the cursor shape.
<value> has starting line in upper byte, ending line in lower byte.
Returns the previous cursor value. */
unsigned cursor(unsigned value)
{
union REGS inregs, outregs;
int ret;
inregs.h.ah = 3; /* Get old cursor */
inregs.h.bh = 0;
int86(0x10,&inregs,&outregs);
ret = outregs.x.cx;
inregs.h.ah = 1; /* Set new cursor */
inregs.x.cx = value;
int86(0x10,&inregs,&outregs);
return(ret);
}