home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
c_news
/
13
/
proj1.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-12-27
|
48KB
|
1,729 lines
/* PROJ1.C
*
* Scott R. Houck
* Written in Turbo C 2.0
*
* Add egavga.bgi to graphics.lib. Then compile with:
*
* tcc proj1 graphics.lib
*
* This is a simple interactive graphics program. Two symbols exist:
* a square and a triangle. Operations are:
*
* create a symbol
* rotate a symbol
* move a symbol
* delete a symbol
* delete all symbols
* print data structure
* terminate program
*
* Assume that your "room" is square, 15 feet on each side. The symbol
* called "square" is one foot on each side, while the triangle is one
* foot on a side.
*
* The following Turbo C graphics functions are used in this program:
*
* cleardevice Clears the screen (active page)
* clearviewport Clears the current viewport
* closegraph Shuts down the graphics system
* drawpoly Draws the outline of a polygon
* floodfill Flood-fills a bounded region
* getcolor Returns the current drawing color
* getfillsettings Returns information about the current fill settings
* getimage Saves a bit image of the specified region to memory
* getmaxx Returns the current x resolution
* getmaxy Returns the current y resolution
* getviewsettings Returns information about the current viewport
* grapherrormsg Returns an error message for specified error code
* graphresult Returns an error code for last error
* imagesize Retuns number of bytes needed to store an image
* initgraph Initializes the graphics system
* lineto Draws a line from the current position to (x,y)
* moveto Moves the current position to (x,y)
* outtext Sends a string to the screen at the current position
* putimage Puts a previously saved bit image onto the screen
* rectangle Draws a rectangle
* registerbgidriver Registers a driver file for inclusion at link time
* setactivepage Sets the active page for graphics output
* setcolor Sets the current drawing color
* setfillstyle Sets the fill pattern and fill color
* settextjustify Set text justification values used by outtext
* setviewport Sets the current output viewport for graphics output
* setvisualpage Sets the visual graphics page number
*/
#include <stdio.h>
#include <math.h>
#include <dos.h>
#include <mem.h>
#include <alloc.h>
#include <graphics.h>
/* Mouse buttons */
#define LEFTBUTTON 1
#define RIGHTBUTTON 2
/* Clipping */
#define CLIPPING_ON 1
#define CLIPPING_OFF 0
/* Boolean values */
#define TRUE 1
#define FALSE 0
/* Dialog box options */
#define YES 1
#define NO 0
/* Symbols */
#define TRIANGLE 0
#define SQUARE 1
/* Menu options */
#define IGNORE -1
#define CREATE 0
#define DELETE 1
#define MOVE 2
#define ROTATE 3
#define DELETE_ALL 4
#define PRINT_DATA 5
#define CANCEL 6
#define QUIT 7
/* Some useful typedefs */
typedef struct {
double x, y;
} DPOINT; /* A point with x and y as doubles */
typedef struct pointtype POINT; /* A point with x and y as ints */
typedef POINT EXTENT [2]; /* Rectangular extent */
typedef double MATRIX [3][3]; /* A 3x3 matrix */
typedef double VECTOR [3]; /* A 1x3 matrix */
/* Menu Box structure */
struct {
EXTENT extent;
char *text;
} menuBox[8];
/* Object structure */
typedef struct {
int numverts; /* Number of vertices */
DPOINT vertex[4]; /* At most 4 vertices */
} OBJECT;
/* For linked list of objects */
typedef struct node NODE, *NODEPTR;
struct node {
OBJECT object;
NODEPTR next;
};
/* Function prototypes */
void InitializeGraphics(void);
void DrawScreen(void);
void Beep(void);
int MouseReset(int *);
void MouseOn(void);
void MouseOff(void);
int MouseStatus(POINT *);
void MouseWaitForPress(int, POINT *);
void MouseWaitForRelease(int, POINT *);
void MouseSetHorizPos(int, int);
void MouseSetVertPos(int, int);
void MouseRestrict(void);
void MouseFree(void);
void MMmult(MATRIX, MATRIX, MATRIX);
void VMmult(VECTOR, MATRIX, VECTOR);
void CalcW2Vmatrix(double, double, double, double,
int, int, int, int, MATRIX);
void CalcV2Wmatrix(int, int, int, int,
double, double, double, double, MATRIX);
void DrawObject(OBJECT);
void Translate(VECTOR, double, double, VECTOR);
void TranslateObject(OBJECT *, double, double);
void Rotate(VECTOR, double, DPOINT, VECTOR);
void RotateObject(OBJECT *, double);
int PointInExtent(POINT, EXTENT);
int PickCorrInMenu(POINT);
int HandlePickInMenu(POINT);
void Message(char *);
void HighlightMenu(int, int, int);
void HighlightSymbol(int, int, int);
int DialogBox(void);
NODEPTR AddList(OBJECT);
void DeleteList(NODEPTR);
NODEPTR SearchList(POINT);
void DoCreateObject(void);
void DoDeleteObject(void);
void DoMoveObject(void);
void DoRotateObject(void);
void DoDeleteAll(void);
void DoPrintData(void);
void DoCancel(void);
int DoQuit(void);
/* Global variables */
MATRIX W2V; /* Window to Viewport matrix */
MATRIX V2W; /* Viewport to Window matrix */
int maxx, maxy; /* Maximum pixel values */
int mminx, mminy, mmaxx, mmaxy; /* Message area coordinates */
int vminx, vminy, vmaxx, vmaxy; /* "Room" (viewport) coords */
int dminx, dminy, dmaxx, dmaxy; /* Dialog box coordinates */
int triangleSymbol[8]; /* Triangle symbol */
int squareSymbol[10]; /* Square symbol */
EXTENT triangleExtent; /* Extent of the triangle symbol */
EXTENT squareExtent; /* Extent of the square symbol */
EXTENT roomExtent; /* Extent of the room */
EXTENT yesExtent, noExtent; /* Extent of the YES/NO buttons */
POINT triangleCenter, squareCenter; /* Centers of the symbols */
NODEPTR list = NULL; /* Pointer to the linked list */
NODEPTR listtail = NULL; /* Pointer to the end of the list */
unsigned dialogSize; /* Dialog box image size */
void *dialogBuffer, *saveBuffer; /* Dialog box and screen buffers */
main()
{
POINT position;
int choice;
InitializeGraphics();
DrawScreen();
MouseOn();
do
{
Message("Choose a menu box with the left mouse button");
MouseWaitForPress(LEFTBUTTON, &position);
MouseWaitForRelease(LEFTBUTTON, &position);
choice = HandlePickInMenu(position);
}
while (choice != QUIT);
MouseOff();
closegraph();
}
/* InitializeGraphics() initializes the graphics package and mouse driver.
* If there is an error, the program aborts with an appropriate error
* message.
*/
void InitializeGraphics()
{
int graphDriver, graphMode, errorCode;
int numberOfButtons;
if (MouseReset(&numberOfButtons) == 0)
{
printf("Mouse driver is not installed\n");
exit(1);
}
if (numberOfButtons < 2)
{
printf("This program requires a mouse with at least two buttons\n");
exit(2);
}
if (registerbgidriver(EGAVGA_driver) < 0)
exit(1);
graphDriver = DETECT;
initgraph(&graphDriver, &graphMode, "");
errorCode = graphresult();
if (errorCode != grOk)
{
printf("Graphics System Error: %s\n", grapherrormsg(errorCode));
exit(1);
}
}
/* DrawScreen() draws the screen. Coordinates are calculated based on
* the current screen resolution so that the program will run on either
* a VGA or EGA system without modification.
*/
void DrawScreen()
{
int vwidth, vheight; /* Viewport */
int bminx, bminy, bmaxx, bmaxy; /* Menu buttons */
int bwidth, bheight, bgap, bstart; /* Menu buttons */
int dwidth, dheight; /* Dialog box */
int yminx, yminy, ymaxx, ymaxy; /* YES button */
int nminx, nminy, nmaxx, nmaxy; /* NO button */
int i;
static char *boxText[] = { "CREATE", "DELETE", "MOVE", "ROTATE",
"DELETE ALL", "PRINT DATA", "CANCEL", "QUIT" };
/* Set the pages to draw the screen in the background */
setactivepage(0);
setvisualpage(1);
/* Get maximum x and y screen coordinates */
maxx = getmaxx();
maxy = getmaxy();
/* Calculate the coordinates of the room */
roomExtent[0].x = vminx = (int)(0.30 * maxx);
roomExtent[0].y = vminy = (int)(0.05 * maxy);
roomExtent[1].x = vmaxx = (int)(0.95 * maxx);
roomExtent[1].y = vmaxy = (int)(0.80 * maxy);
vwidth = vmaxx - vminx;
vheight = vmaxy - vminy;
/* Calculate the coordinates of the message area */
mminx = vminx;
mminy = (int)(0.85 * maxy);
mmaxx = vmaxx;
mmaxy = (int)(0.95 * maxy);
/* Draw the dialog box */
dwidth = (int)(0.35 * vwidth);
dheight = (int)(0.25 * vheight);
dminx = vminx + (vwidth - dwidth) / 2;
dminy = vminy + (vheight - dheight) / 2;
dmaxx = dminx + dwidth;
dmaxy = dminy + dheight;
rectangle(dminx, dminy, dmaxx, dmaxy); /* The outline */
yesExtent[0].x = yminx = dminx + (int)(0.10 * dwidth);
yesExtent[0].y = yminy = dminy + (int)(0.60 * dheight);
yesExtent[1].x = ymaxx = dminx + (int)(0.40 * dwidth);
yesExtent[1].y = ymaxy = dminy + (int)(0.90 * dheight);
rectangle(yminx, yminy, ymaxx, ymaxy);
setfillstyle(SOLID_FILL, RED);
floodfill(yminx+1, yminy+1, WHITE); /* YES button */
noExtent[0].x = nminx = dminx + (int)(0.60 * dwidth);
noExtent[0].y = nminy = yminy;
noExtent[1].x = nmaxx = dminx + (int)(0.90 * dwidth);
noExtent[1].y = nmaxy = ymaxy;
rectangle(nminx, nminy, nmaxx, nmaxy);
floodfill(nminx+1, nminy+1, WHITE); /* NO button */
setfillstyle(SOLID_FILL, LIGHTGRAY);
floodfill(dminx+1, dminy+1, WHITE); /* The interior */
settextjustify(CENTER_TEXT, CENTER_TEXT);
moveto(dminx+dwidth/2, dminy+(yminy-dminy)/2);
setcolor(BLACK);
outtext("Are you sure?");
setcolor(WHITE);
moveto((yminx+ymaxx)/2, (yminy+ymaxy)/2);
outtext("YES");
moveto((nminx+nmaxx)/2, (nminy+nmaxy)/2);
outtext("NO");
setcolor(WHITE);
/* Save dialog box in memory */
dialogSize = imagesize(dminx, dminy, dmaxx, dmaxy);
dialogBuffer = malloc(dialogSize);
getimage(dminx, dminy, dmaxx, dmaxy, dialogBuffer);
cleardevice(); /* Clear the screen */
/* Draw the menu buttons */
bheight = (int)(0.06 * maxy);
bwidth = (int)(0.15 * maxx);
bgap = (int)(0.03 * maxy);
bstart = (int)(0.05 * maxy);
setfillstyle(SOLID_FILL, RED);
bminx = (int)(0.05 * maxx);
for (i = 0; i < 8; i++)
{
bminy = (int)(i * (bheight + bgap)) + bstart;
bmaxx = bminx + bwidth;
bmaxy = bminy + bheight;
rectangle(bminx, bminy, bmaxx, bmaxy);
floodfill(bminx+1, bminy+1, WHITE);
moveto(bminx+bwidth/2, bminy+bheight/2);
outtext(boxText[i]);
/* Initialize the menuBox info */
menuBox[i].extent[0].x = bminx;
menuBox[i].extent[0].y = bminy;
menuBox[i].extent[1].x = bmaxx;
menuBox[i].extent[1].y = bmaxy;
menuBox[i].text = boxText[i];
}
/* Draw the triangle symbol */
bminx = (int)(0.03 * maxx);
bminy = (int)(0.85 * maxy);
bmaxy = (int)(0.95 * maxy);
bwidth = (int)(0.07 * maxx);
bheight = bmaxy - bminy;
triangleSymbol[0] = bminx;
triangleSymbol[1] = bmaxy;
triangleSymbol[2] = bminx + bwidth;
triangleSymbol[3] = bmaxy;
triangleSymbol[4] = bminx + bwidth/2;
triangleSymbol[5] = bminy;
triangleSymbol[6] = bminx;
triangleSymbol[7] = bmaxy;
triangleCenter.x = bminx + bwidth/2;
triangleCenter.y = bminy + bheight/2;
drawpoly(4, triangleSymbol);
setfillstyle(SOLID_FILL, MAGENTA);
floodfill(bminx + bwidth/2, bminy + bheight/2, WHITE);
/* Initialize the triangle symbol's extent */
triangleExtent[0].x = bminx;
triangleExtent[0].y = bminy;
triangleExtent[1].x = bminx + bwidth;
triangleExtent[1].y = bmaxy;
/* Draw the square symbol */
bgap = (int)(0.05 * maxx);
bminx += bwidth + bgap;
squareSymbol[0] = bminx;
squareSymbol[1] = bmaxy;
squareSymbol[2] = bminx + bwidth;
squareSymbol[3] = bmaxy;
squareSymbol[4] = bminx + bwidth;
squareSymbol[5] = bminy;
squareSymbol[6] = bminx;
squareSymbol[7] = bminy;
squareSymbol[8] = bminx;
squareSymbol[9] = bmaxy;
squareCenter.x = bminx + bwidth/2;
squareCenter.y = bminy + bheight/2;
drawpoly(5, squareSymbol);
setfillstyle(SOLID_FILL, GREEN);
floodfill(bminx + bwidth/2, bminy + bheight/2, WHITE);
/* Initialize the square symbol's extent */
squareExtent[0].x = bminx;
squareExtent[0].y = bminy;
squareExtent[1].x = bminx + bwidth;
squareExtent[1].y = bmaxy;
/* Calculate window-to-viewport and viewport-to-window matrices */
CalcW2Vmatrix(0.0, 0.0, 15.0, 15.0, vminx, vminy, vmaxx, vmaxy, W2V);
CalcV2Wmatrix(vminx, vminy, vmaxx, vmaxy, 0.0, 0.0, 15.0, 15.0, V2W);
/* Draw the message area */
setfillstyle(SOLID_FILL, CYAN);
rectangle(mminx, mminy, mmaxx, mmaxy);
floodfill(mminx+1, mminy+1, WHITE);
/* Draw the room */
setfillstyle(SOLID_FILL, BLUE);
rectangle(vminx-1, vminy-1, vmaxx+1, vmaxy+1);
floodfill(vminx, vminy, WHITE);
/* Set viewport to the room */
setviewport(vminx, vminy, vmaxx, vmaxy, CLIPPING_ON);
/* Display screen */
setvisualpage(0);
}
/* Beep() sounds a beep to indicate an error. */
void Beep()
{
sound(150);
delay(75);
nosound();
}
/* MouseReset() returns the current status of the mouse hardware and
* software. The mouse status is 0 if the mouse hardware and software
* are not installed or -1 if the hardware and software are installed.
*
* This function also resets the mouse driver to the default values.
* The number of buttons is returned in numberOfButtons.
*/
int MouseReset(int *numberOfButtons)
{
union REGS inregs, outregs;
inregs.x.ax = 0; /* Mouse Function 0 -- Mouse Reset and Status */
int86(0x33, &inregs, &outregs);
*numberOfButtons = outregs.x.bx;
return outregs.x.ax;
}
/* MouseOn() shows the mouse cursor. */
void MouseOn()
{
union REGS inregs, outregs;
inregs.x.ax = 1; /* Mouse Function 1 -- Show Cursor */
int86(0x33, &inregs, &outregs);
}
/* MouseOff() hides the mouse cursor. */
void MouseOff()
{
union REGS inregs, outregs;
inregs.x.ax = 2; /* Mouse function 2 -- Hide Cursor */
int86(0x33, &inregs, &outregs);
}
/* MouseStatus() returns the state of the left and right mouse buttons
* and the horizontal and vertical coordinates of the cursor.
*
* The button status is a single integer value. Bit 0 represents the
* left button; bit 1 represents the right button. These bits are 1
* if the corresponding button is down, and 0 if it is up.
*/
int MouseStatus(POINT *position)
{
union REGS inregs, outregs;
inregs.x.ax = 3; /* Mouse function 3 --
Get Button Status and Mouse Position */
int86(0x33, &inregs, &outregs);
position->x = outregs.x.cx;
position->y = outregs.x.dx;
return outregs.x.bx; /* Button status */
}
/* MouseWaitForPress() puts the program in a wait state until the
* user presses the specified mouse button.
*/
void MouseWaitForPress(int button, POINT *posptr)
{
int buttonPressed;
do
buttonPressed = MouseStatus(posptr);
while (!(buttonPressed & button));
}
/* MouseWaitForRelease() puts the program in a wait state until the
* user releases the specified mouse button.
*/
void MouseWaitForRelease(int button, POINT *posptr)
{
int buttonPressed;
do
buttonPressed = MouseStatus(posptr);
while (buttonPressed & button);
}
/* MouseSetHorizPos() sets the minimum and maximum horizontal cursor
* coordinates on the screen. All cursor movement after the call
* is restricted to the specified area.
*/
void MouseSetHorizPos(int minpos, int maxpos)
{
union REGS inregs, outregs;
inregs.x.ax = 7; /* Mouse function 7 -- Set Minimum and Maximum
Horizontal Cursor Position */
inregs.x.cx = minpos;
inregs.x.dx = maxpos;
int86(0x33, &inregs, &outregs);
}
/* MouseSetVertPos() sets the minimum and maximum vertical cursor
* coordinates on the screen. All cursor movement after the call
* is restricted to the specified area.
*/
void MouseSetVertPos(int minpos, int maxpos)
{
union REGS inregs, outregs;
inregs.x.ax = 8; /* Mouse function 8 -- Set Minimum and Maximum
Vertical Cursor Position */
inregs.x.cx = minpos;
inregs.x.dx = maxpos;
int86(0x33, &inregs, &outregs);
}
/* MouseRestrict() restricts the cursor to the viewport. */
void MouseRestrict()
{
MouseSetHorizPos(vminx, vmaxx);
MouseSetVertPos(vminy, vmaxy);
}
/* MouseFree() frees the cursor to move about the entire screen. */
void MouseFree()
{
MouseSetHorizPos(0, maxx);
MouseSetVertPos(0, maxy);
}
/* MMmult() performs Matrix-Matrix multiplication.
*
* _ _ _ _ _ _
* | | | | | |
* | a a a | | b b b | | c c c |
* | 00 01 02 | | 00 01 02 | | 00 01 02 |
* | | | | | |
* | a a a | | b b b | | c c c |
* | 10 11 12 | x | 10 11 12 | = | 10 11 12 |
* | | | | | |
* | a a a | | b b b | | c c c |
* | 20 21 22 | | 20 21 22 | | 20 21 22 |
* |_ _| |_ _| |_ _|
*
*
*/
void MMmult(MATRIX a, MATRIX b, MATRIX c)
{
int i, j, k;
for (i = 0; i < 3; i++) /* Row i */
for (j = 0; j < 3; j++) /* Column j */
for (c[i][j] = 0.0, k = 0; k < 3; k++)
c[i][j] += a[i][k] * b[k][j];
}
/* VMmult() performs Vector-Matrix multiplication.
*
* _ _
* | |
* | b b b |
* _ _ | 00 01 02 | _ _
* | | | | | |
* | a a a | | b b b | | c c c |
* | 0 1 2 | x | 10 11 12 | = | 0 1 2 |
* |_ _| | | |_ _|
* | b b b |
* | 20 21 22 |
* |_ _|
*
*
*/
void VMmult(VECTOR a, MATRIX b, VECTOR c)
{
int i, j;
for (i = 0; i < 3; i++)
for (c[i] = 0.0, j = 0; j < 3; j++)
c[i] += a[j] * b[j][i];
}
/* CalcW2Vmatrix() calculates the Window to Viewport matrix.
*
* Window coordinates: (xmin,ymin) and (xmax,ymax)
* Viewport coordinates: (umin,vmin) and (umax,vmax)
*
* _ _ _ _ _ _
* | || || |
* | || umax-umin || |
* | 1 0 0 || --------- 0 0 || 1 0 0 |
* | || xmax-xmin || |
* | || vmax-vmin || |
* | 0 1 0 || 0 - --------- 0 || 0 1 0 |
* | || ymax-ymin || |
* | || || |
* | -xmin -ymin 1 || 0 0 1 || 0 vmax-vmin 1 |
* | || || |
* |_ _||_ _||_ _|
*
*
* trans to origin scale window to viewport trans to viewport
*
*
*
* Note that these matrices are somewhat different from the normal
* matrices used because the Turbo C graphics package considers the
* origin to be at the upper left corner of the screen (rather than
* the lower left) and also because the setviewport() function changes
* the logical coordinates of the viewport to (0,0).
*/
void CalcW2Vmatrix(xmin, ymin, xmax, ymax, umin, vmin, umax, vmax, m)
double xmin, ymin, xmax, ymax; /* Window coordinates */
int umin, vmin, umax, vmax; /* Viewport coordinates */
MATRIX m; /* Resulting Window to Viewport matrix */
{
MATRIX a, b;
/* Translate window to origin */
a[0][0] = a[1][1] = a[2][2] = 1.0;
a[0][1] = a[0][2] = a[1][0] = a[1][2] = 0.0;
a[2][0] = -xmin;
a[2][1] = -ymin;
/* Scale window into viewport */
m[0][1] = m[0][2] = m[1][0] = m[1][2] = m[2][0] = m[2][1] = 0.0;
m[2][2] = 1.0;
m[0][0] = (double)(umax - umin) / (xmax - xmin);
m[1][1] = -(double)(vmax - vmin) / (ymax - ymin);
MMmult(a, m, b);
/* Translate to viewport position
*
* Normally, you would translate back to (umin,vmin), but Turbo C's
* setviewport() function treats the upper left corner of the viewport
* as (0,0). We want the origin to be in the lower left corner, so we
* still have to translate to viewport_height - y. We scaled by -1
* above (to get the -y), so all we have to do is translate by the
* viewport height (vmax-vmin).
*/
a[2][0] = 0.0;
a[2][1] = (double)(vmax - vmin);
MMmult(b, a, m);
}
/* CalcV2Wmatrix() calculates the Viewport to Window matrix.
*
* Viewport coordinates: (umin,vmin) and (umax,vmax)
* Window coordinates: (xmin,ymin) and (xmax,ymax)
*
* _ _ _ _ _ _
* | || || |
* | || xmax-xmin || |
* | 1 0 0 || --------- 0 0 || 1 0 0 |
* | || umax-umin || |
* | || ymax-ymin || |
* | 0 1 0 || 0 - --------- 0 || 0 1 0 |
* | || vmax-vmin || |
* | || || |
* | 0 -(vmax-vmin) 1 || 0 0 1 || xmin ymin 1 |
* | || || |
* |_ _||_ _||_ _|
*
*
* trans to origin scale viewport to window trans to window
*
*
*
* Note that these matrices are somewhat different from the normal
* matrices used because the Turbo C graphics package considers the
* origin to be at the upper left corner of the screen (rather than
* the lower left) and also because the setviewport() function changes
* the logical coordinates of the viewport to (0,0).
*/
void CalcV2Wmatrix(umin, vmin, umax, vmax, xmin, ymin, xmax, ymax, m)
int umin, vmin, umax, vmax; /* Viewport coordinates */
double xmin, ymin, xmax, ymax; /* Window coordinates */
MATRIX m; /* Resulting Viewport to Window matrix */
{
MATRIX a, b;
/* Translate viewport to origin */
a[0][0] = a[1][1] = a[2][2] = 1.0;
a[0][1] = a[0][2] = a[1][0] = a[1][2] = a[2][0] = 0.0;
a[2][1] = -(double)(vmax - vmin);
/* Scale viewport to window */
m[0][1] = m[0][2] = m[1][0] = m[1][2] = m[2][0] = m[2][1] = 0.0;
m[2][2] = 1.0;
m[0][0] = (xmax - xmin) / (double)(umax - umin);
m[1][1] = -(ymax - ymin) / (double)(vmax - vmin);
MMmult(a, m, b);
/* Translate to window position */
a[2][0] = xmin;
a[2][1] = ymin;
MMmult(b, a, m);
}
/* DrawObject() draws the specified object on the screen. */
void DrawObject(OBJECT object)
{
VECTOR p, q;
int i;
p[2] = 1.0;
/* We will loop one more than the number of vertices in order
* to draw a line from the last vertex to the first vertex.
*/
for (i = 0; i < object.numverts + 1; i++)
{
p[0] = object.vertex[i % object.numverts].x;
p[1] = object.vertex[i % object.numverts].y;
VMmult(p, W2V, q); /* Transform to viewport coords */
if (i == 0)
moveto((int)q[0], (int)q[1]);
else
lineto((int)q[0], (int)q[1]);
}
}
/* Translate() translates a point contained in VECTOR p by the amounts
* Dx and Dy. The new point is contained in VECTOR q.
*
* _ _
* | |
* | 1 0 0 |
* _ _ | | _ _
* | | | | | |
* | p p p | | 0 1 0 | | q q q |
* | 0 1 2 | x | | = | 0 1 2 |
* |_ _| | | |_ _|
* | Dx Dy 1 |
* | |
* |_ _|
*
*
*/
void Translate(VECTOR p, double Dx, double Dy, VECTOR q)
{
MATRIX t;
t[0][0] = t[1][1] = t[2][2] = 1.0;
t[0][1] = t[0][2] = t[1][0] = t[1][2] = 0.0;
t[2][0] = Dx;
t[2][1] = Dy;
VMmult(p, t, q);
}
/* TranslateObject() calls Translate() to translate all the vertices of
* the specified object by the amounts Dx and Dy.
*/
void TranslateObject(OBJECT *object, double Dx, double Dy)
{
int i;
VECTOR p, q;
p[2] = 1.0;
for (i = 0; i < object->numverts; i++)
{
p[0] = object->vertex[i].x;
p[1] = object->vertex[i].y;
Translate(p, Dx, Dy, q);
object->vertex[i].x = q[0];
object->vertex[i].y = q[1];
}
}
/* Rotate() rotates a point contained in VECTOR p about the point pt.
* The new point is contained in VECTOR q.
*
* _ _ _ _ _ _
* | || || |
* | || || |
* | 1 0 0 || cos(angle) sin(angle) 0 || 1 0 0 |
* | || || |
* | || || |
* | 0 1 0 || -sin(angle) cos(angle) 0 || 0 1 0 |
* | || || |
* | || || |
* | -pt.x -pt.y 1 || 0 0 1 || pt.x pt.y 1 |
* | || || |
* |_ _||_ _||_ _|
*
*
* trans pt to origin rotate trans back to pt
*
*/
void Rotate(VECTOR p, double angle, DPOINT pt, VECTOR q)
{
MATRIX r, t, u;
/* Translate point to origin */
t[0][0] = t[1][1] = t[2][2] = 1.0;
t[0][1] = t[0][2] = t[1][0] = t[1][2] = 0.0;
t[2][0] = -pt.x;
t[2][1] = -pt.y;
/* Define the rotation matrix. Note that angle is in radians. */
r[0][2] = r[1][2] = r[2][0] = r[2][1] = 0.0;
r[2][2] = 1.0;
r[0][0] = r[1][1] = cos(angle);
r[0][1] = sin(angle);
r[1][0] = -r[0][1];
MMmult(t, r, u);
/* Translate back to point */
t[2][0] = pt.x;
t[2][1] = pt.y;
MMmult(u, t, r);
/* r now contains the accumulated matrix for the rotation */
VMmult(p, r, q);
}
/* RotateObject() calls Translate() to translate all the vertices of
* the specified object by the amounts Dx and Dy.
*/
void RotateObject(OBJECT *object, double degrees)
{
int i;
VECTOR p, q;
DPOINT center;
double radians = degrees * (M_PI / 180.0);
/* Calculate the center of the object */
center.x = center.y = 0.0;
for (i = 0; i < object->numverts; i++)
{
center.x += object->vertex[i].x;
center.y += object->vertex[i].y;
}
center.x /= object->numverts;
center.y /= object->numverts;
p[2] = 1.0;
for (i = 0; i < object->numverts; i++)
{
p[0] = object->vertex[i].x;
p[1] = object->vertex[i].y;
Rotate(p, radians, center, q);
object->vertex[i].x = q[0];
object->vertex[i].y = q[1];
}
}
/* PointInExtent() returns a boolean value specifying whether a point
* is in a certain extent.
*/
int PointInExtent(POINT pt, EXTENT ext)
{
return ext[0].x <= pt.x && pt.x <= ext[1].x &&
ext[0].y <= pt.y && pt.y <= ext[1].y;
}
/* PickCorrInMenu() performs pick correlation within the menu box area. */
int PickCorrInMenu(POINT pt)
{
int i;
for (i = 0; i < 8; i++)
if (PointInExtent(pt, menuBox[i].extent))
return i;
return IGNORE;
}
/* HandlePickInMenu() calls the appropriate function to handle a menu
* option chosen by the user.
*/
int HandlePickInMenu(POINT pt)
{
int choice;
switch (choice = PickCorrInMenu(pt))
{
case IGNORE:
Beep();
break;
case CREATE:
DoCreateObject();
break;
case DELETE:
DoDeleteObject();
break;
case MOVE:
DoMoveObject();
break;
case ROTATE:
DoRotateObject();
break;
case DELETE_ALL:
DoDeleteAll();
break;
case PRINT_DATA:
DoPrintData();
break;
case CANCEL:
DoCancel();
break;
case QUIT:
choice = DoQuit();
break;
}
return choice;
}
/* Message() prints a message in the message area of screen. */
void Message(char *msg)
{
struct viewporttype view;
int color;
int msgx = mminx + 15;
int msgy = (mminy + mmaxy) / 2;
char temp[50];
MouseOff();
/* Get attributes */
getviewsettings(&view);
color = getcolor();
setviewport(0, 0, maxx, maxy, CLIPPING_OFF); /* Entire screen */
settextjustify(LEFT_TEXT, CENTER_TEXT);
memset(temp, 219, 49); /* Character 219 is a block */
temp[49] = '\0';
setcolor(CYAN);
moveto(msgx, msgy);
outtext(temp); /* Blank out existing message */
setcolor(BLACK);
moveto(msgx, msgy);
outtext(msg); /* Print message */
/* Reset attributes */
setviewport(view.left, view.top, view.right, view.bottom, view.clip);
setcolor(color);
MouseOn();
}
/* HighlightMenu() highlights the specified menu box. */
void HighlightMenu(int box, int background, int foreground)
{
struct viewporttype view;
struct fillsettingstype fill;
int color;
int bminx = menuBox[box].extent[0].x;
int bminy = menuBox[box].extent[0].y;
int bmaxx = menuBox[box].extent[1].x;
int bmaxy = menuBox[box].extent[1].y;
MouseOff();
/* Get attributes */
getviewsettings(&view);
getfillsettings(&fill);
color = getcolor();
setviewport(bminx, bminy, bmaxx, bmaxy, CLIPPING_OFF);
clearviewport();
setviewport(0, 0, maxx, maxy, CLIPPING_OFF);
rectangle(bminx, bminy, bmaxx, bmaxy);
setfillstyle(SOLID_FILL, background);
floodfill(bminx+1, bminy+1, WHITE);
settextjustify(CENTER_TEXT, CENTER_TEXT);
setcolor(foreground);
moveto((bminx + bmaxx) / 2, (bminy + bmaxy) / 2);
outtext(menuBox[box].text);
/* Reset attributes */
setviewport(view.left, view.top, view.right, view.bottom, view.clip);
setfillstyle(fill.pattern, fill.color);
setcolor(color);
MouseOn();
}
/* HighlightSymbol() highlights the specified symbol chosen by the user
* during the CREATE process.
*/
void HighlightSymbol(int symbol, int fillstyle, int color)
{
struct viewporttype view;
struct fillsettingstype fill;
MouseOff();
getviewsettings(&view);
getfillsettings(&fill);
setviewport(0, 0, maxx, maxy, CLIPPING_OFF);
setfillstyle(fillstyle, color);
if (symbol == TRIANGLE)
floodfill(triangleCenter.x, triangleCenter.y, WHITE);
else /* SQUARE */
floodfill(squareCenter.x, squareCenter.y, WHITE);
setviewport(view.left, view.top, view.right, view.bottom, view.clip);
setfillstyle(fill.pattern, fill.color);
MouseOn();
}
/* DialogBox() displays a YES/NO dialog box, receives the input from
* the user, restores the screen, and returns either YES or NO to the
* caller.
*/
int DialogBox()
{
POINT position;
int choice, done = FALSE;
struct viewporttype view;
MouseOff();
getviewsettings(&view);
setviewport(0, 0, maxx, maxy, CLIPPING_OFF); /* Entire screen */
/* Save the screen area under where the dialog box will display */
saveBuffer = malloc(dialogSize);
getimage(dminx, dminy, dmaxx, dmaxy, saveBuffer);
/* Display the dialog box */
putimage(dminx, dminy, dialogBuffer, COPY_PUT);
MouseOn();
Message("Choose YES or NO using left mouse button");
do
{
MouseWaitForPress(LEFTBUTTON, &position);
MouseWaitForRelease(LEFTBUTTON, &position);
if (PointInExtent(position, yesExtent))
{
choice = YES;
done = TRUE;
}
else if (PointInExtent(position, noExtent))
{
choice = NO;
done = TRUE;
}
}
while (!done);
/* Restore the screen area and free the buffer */
MouseOff();
putimage(dminx, dminy, saveBuffer, COPY_PUT);
free(saveBuffer);
MouseOn();
setviewport(view.left, view.top, view.right, view.bottom, view.clip);
return choice;
}
/* AddList() adds an object to the linked list. */
NODEPTR AddList(OBJECT object)
{
NODEPTR newnode;
newnode = (NODEPTR) malloc(sizeof(NODE));
if (newnode)
{
newnode->object = object;
newnode->next = NULL;
if (!list)
list = listtail = newnode;
else
listtail = listtail->next = newnode;
}
return newnode;
}
/* DeleteList() deletes an object from the linked list. */
void DeleteList(NODEPTR ptr)
{
NODEPTR p;
if (ptr == list)
list = ptr->next;
else if (ptr == listtail)
{
for (p = list; p->next != listtail; p = p->next)
;
p->next = NULL;
listtail = p;
}
else
{
for (p = list; p->next != ptr; p = p->next)
;
p->next = ptr->next;
}
free(ptr);
}
/* SearchList() searches the list and returns a pointer to the object
* in the list if the point lies in the extent of the object. If no
* object is found, NULL is returned.
*/
NODEPTR SearchList(POINT pt)
{
NODEPTR p;
VECTOR vcoords[4], wcoords[4];
EXTENT extent;
int i, x, y;
for (p = list; p; p = p->next)
{
/* Transform the object's vertices into physical device
* coordinates and calculate its extent.
*/
extent[0].x = maxx;
extent[0].y = maxy;
extent[1].x = 0;
extent[1].y = 0;
for (i = 0; i < p->object.numverts; i++)
{
wcoords[i][0] = p->object.vertex[i].x;
wcoords[i][1] = p->object.vertex[i].y;
wcoords[i][2] = 1.0;
VMmult(wcoords[i], W2V, vcoords[i]);
vcoords[i][0] += vminx;
vcoords[i][1] += vminy;
if ((x = (int) vcoords[i][0]) < extent[0].x) extent[0].x = x;
if ((y = (int) vcoords[i][1]) < extent[0].y) extent[0].y = y;
if ((x = (int) vcoords[i][0]) > extent[1].x) extent[1].x = x;
if ((y = (int) vcoords[i][1]) > extent[1].y) extent[1].y = y;
}
if (PointInExtent(pt, extent))
return p; /* Object was found */
}
return NULL; /* Object was not found at that position */
}
/* DoCreateObject() handles the CREATE menu option. */
void DoCreateObject()
{
POINT position;
VECTOR vcoords, wcoords;
OBJECT object;
int symbol, normalColor;
HighlightMenu(CREATE, LIGHTGRAY, BLACK);
Message("Choose a TRIANGLE or SQUARE with left button");
while (1)
{
MouseWaitForPress(LEFTBUTTON, &position);
MouseWaitForRelease(LEFTBUTTON, &position);
if (PointInExtent(position, menuBox[CANCEL].extent))
{
HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
HighlightMenu(CREATE, RED, WHITE);
HighlightMenu(CANCEL, RED, WHITE);
return;
}
if (PointInExtent(position, triangleExtent))
{
symbol = TRIANGLE;
break;
}
else if (PointInExtent(position, squareExtent))
{
symbol = SQUARE;
break;
}
else
Beep(); /* Invalid click */
}
normalColor = (symbol == TRIANGLE) ? MAGENTA : GREEN;
HighlightSymbol(symbol, INTERLEAVE_FILL, LIGHTGRAY);
Message("Choose location in \"room\" with left button");
while (1)
{
MouseWaitForPress(LEFTBUTTON, &position);
MouseWaitForRelease(LEFTBUTTON, &position);
if (PointInExtent(position, menuBox[CANCEL].extent))
{
HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
HighlightSymbol(symbol, SOLID_FILL, normalColor);
HighlightMenu(CREATE, RED, WHITE);
HighlightMenu(CANCEL, RED, WHITE);
return;
}
if (PointInExtent(position, roomExtent))
break;
else
Beep(); /* Invalid click */
}
vcoords[0] = position.x - vminx;
vcoords[1] = position.y - vminy;
vcoords[2] = 1.0;
VMmult(vcoords, V2W, wcoords);
/* wcoords is the bottom left corner of the object */
object.vertex[0].x = wcoords[0];
object.vertex[0].y = wcoords[1];
object.vertex[1].x = wcoords[0] + 1.0;
object.vertex[1].y = wcoords[1];
if (symbol == TRIANGLE)
{
object.vertex[2].x = wcoords[0] + 0.5;
object.vertex[2].y = wcoords[1] + 1.0;
object.numverts = 3;
}
else /* SQUARE */
{
object.vertex[2].x = wcoords[0] + 1.0;
object.vertex[2].y = wcoords[1] + 1.0;
object.vertex[3].x = wcoords[0];
object.vertex[3].y = wcoords[1] + 1.0;
object.numverts = 4;
}
AddList(object);
MouseOff();
DrawObject(object);
MouseOn();
HighlightSymbol(symbol, SOLID_FILL, normalColor);
HighlightMenu(CREATE, RED, WHITE);
}
/* DoDeleteObject() handles the DELETE menu option. */
void DoDeleteObject()
{
POINT position;
NODEPTR p, ptr;
char *mainMessage = "Select the object to be deleted using left button";
HighlightMenu(DELETE, LIGHTGRAY, BLACK);
if (!list) /* No objects to be deleted */
{
Beep();
Message("No objects to be deleted");
sleep(1);
HighlightMenu(DELETE, RED, WHITE);
return;
}
Message(mainMessage);
while (1)
{
MouseWaitForPress(LEFTBUTTON, &position);
MouseWaitForRelease(LEFTBUTTON, &position);
if (PointInExtent(position, menuBox[CANCEL].extent))
{
HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
HighlightMenu(DELETE, RED, WHITE);
HighlightMenu(CANCEL, RED, WHITE);
return;
}
if ((ptr = SearchList(position)) == NULL)
{
Beep();
Message("No object found there");
sleep(1);
Message(mainMessage);
}
else
break; /* Object found */
}
/* Erase the object */
MouseOff();
setcolor(BLUE);
DrawObject(ptr->object);
setcolor(WHITE);
/* Delete it from the list */
DeleteList(ptr);
/* Redraw all objects in case there was an overlap */
for (p = list; p; p = p->next)
DrawObject(p->object);
MouseOn();
HighlightMenu(DELETE, RED, WHITE);
}
/* DoMoveObject() handles the MOVE menu option. */
void DoMoveObject()
{
POINT position;
NODEPTR p, ptr;
VECTOR vcoords, wcoords;
int i, valid;
double Dx, Dy, deltaX, deltaY;
int buttonPressed;
char *mainMessage = "Drag an object with left button, then release";
HighlightMenu(MOVE, LIGHTGRAY, BLACK);
if (!list) /* No objects to be deleted */
{
Beep();
Message("No objects to be moved");
sleep(1);
HighlightMenu(MOVE, RED, WHITE);
return;
}
Message(mainMessage);
while (1)
{
MouseWaitForPress(LEFTBUTTON, &position);
if (PointInExtent(position, menuBox[CANCEL].extent))
{
MouseWaitForRelease(LEFTBUTTON, &position);
HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
HighlightMenu(MOVE, RED, WHITE);
HighlightMenu(CANCEL, RED, WHITE);
return;
}
if ((ptr = SearchList(position)) == NULL)
{
Beep();
Message("No object found there");
sleep(1);
Message(mainMessage);
MouseWaitForRelease(LEFTBUTTON, &position);
}
else
break; /* Object found */
}
/* Determine x and y distance from clicked position to the bottom
* left corner of the object (deltaX and deltaY). This will be used
* below so that when dragging the object, the cursor will remain on
* the object at the same place.
*/
vcoords[0] = position.x - vminx;
vcoords[1] = position.y - vminy;
vcoords[2] = 1.0;
VMmult(vcoords, V2W, wcoords);
deltaX = wcoords[0] - ptr->object.vertex[0].x;
deltaY = wcoords[1] - ptr->object.vertex[0].y;
/* Restrict the mouse to the "room" */
MouseRestrict();
do
{
buttonPressed = MouseStatus(&position);
/* Convert the clicked position to world coordinates */
vcoords[0] = position.x - vminx;
vcoords[1] = position.y - vminy;
vcoords[2] = 1.0;
VMmult(vcoords, V2W, wcoords);
/* Determine Dx and Dy for the translation */
Dx = wcoords[0] - ptr->object.vertex[0].x - deltaX;
Dy = wcoords[1] - ptr->object.vertex[0].y - deltaY;
/* Erase old position */
MouseOff();
setcolor(BLUE);
DrawObject(ptr->object);
setcolor(WHITE);
/* Translate the object and redraw all objects */
TranslateObject(&ptr->object, Dx, Dy);
for (p = list; p; p = p->next)
DrawObject(p->object);
MouseOn();
}
while (buttonPressed & LEFTBUTTON); /* Wait for release */
MouseFree();
HighlightMenu(MOVE, RED, WHITE);
}
/* DoRotateObject() handles the ROTATE menu option. */
void DoRotateObject()
{
POINT position;
NODEPTR p, ptr;
int buttonPressed;
double degrees;
char *mainMessage = "Select an object to be rotated using left button";
HighlightMenu(ROTATE, LIGHTGRAY, BLACK);
if (!list) /* No objects to be rotated */
{
Beep();
Message("No objects to be rotated");
sleep(1);
HighlightMenu(ROTATE, RED, WHITE);
return;
}
Message(mainMessage);
while (1)
{
MouseWaitForPress(LEFTBUTTON, &position);
MouseWaitForRelease(LEFTBUTTON, &position);
if (PointInExtent(position, menuBox[CANCEL].extent))
{
HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
HighlightMenu(ROTATE, RED, WHITE);
HighlightMenu(CANCEL, RED, WHITE);
return;
}
if ((ptr = SearchList(position)) == NULL)
{
Beep();
Message("No object found there");
sleep(1);
Message(mainMessage);
}
else
break;
}
Message("Use left/right buttons to rotate, QUIT to end");
while (1)
{
buttonPressed = MouseStatus(&position);
if (buttonPressed)
{
if (PointInExtent(position, menuBox[QUIT].extent))
{
HighlightMenu(QUIT, LIGHTGRAY, BLACK);
HighlightMenu(ROTATE, RED, WHITE);
HighlightMenu(QUIT, RED, WHITE);
MouseWaitForRelease(LEFTBUTTON, &position);
return;
}
if (buttonPressed & LEFTBUTTON)
degrees = 2.0;
else
degrees = -2.0;
/* Erase old position */
MouseOff();
setcolor(BLUE);
DrawObject(ptr->object);
setcolor(WHITE);
/* Rotate the object and redraw all objects */
RotateObject(&ptr->object, degrees);
for (p = list; p; p = p->next)
DrawObject(p->object);
MouseOn();
}
}
}
/* DoDeleteAll() handles the DELETE ALL menu option. */
void DoDeleteAll()
{
NODEPTR p, q;
HighlightMenu(DELETE_ALL, LIGHTGRAY, BLACK);
if (!list) /* No objects to be deleted */
{
Beep();
Message("No objects to be deleted");
sleep(1);
HighlightMenu(DELETE_ALL, RED, WHITE);
return;
}
/* After obtaining confirmation through the dialog box, erase
* all objects, free the memory, and make the list null.
*/
if (DialogBox() == YES)
{
MouseOff();
setcolor(BLUE);
p = list;
while (p)
{
q = p;
DrawObject(p->object);
p = p->next;
free(q);
}
list = listtail = NULL;
setcolor(WHITE);
MouseOn();
}
HighlightMenu(DELETE_ALL, RED, WHITE);
}
/* DoPrintData() handles the PRINT DATA menu option. */
void DoPrintData()
{
FILE *outfile;
NODEPTR p;
int i;
HighlightMenu(PRINT_DATA, LIGHTGRAY, BLACK);
if ((outfile = fopen("proj1.dat", "w")) == NULL)
{
Message("Cannot create file PROJ1.DAT");
HighlightMenu(PRINT_DATA, RED, WHITE);
return;
}
if (!list)
fprintf(outfile, "No objects found");
else
for (p = list; p; p = p->next)
{
fprintf(outfile, "Object Type: %s\n",
p->object.numverts == 4 ? "SQUARE" : "TRIANGLE");
fprintf(outfile, "Vertices:\n");
for (i = 0; i < p->object.numverts; i++)
fprintf(outfile, " (%2.2f, %2.2f)\n",
p->object.vertex[i].x, p->object.vertex[i].y);
fprintf(outfile, "\n");
}
fclose(outfile);
Message("File PROJ1.DAT successfully created");
sleep(1);
HighlightMenu(PRINT_DATA, RED, WHITE);
}
/* DoCancel() handles the CANCEL menu option. Note that since CANCEL
* must be used in conjunction with another menu option, this option
* chosen as a standalone function just beeps and returns an error message.
*/
void DoCancel()
{
HighlightMenu(CANCEL, LIGHTGRAY, BLACK);
Beep();
Message("Nothing to cancel!");
sleep(1);
HighlightMenu(CANCEL, RED, WHITE);
}
/* DoQuit() handles the QUIT menu option. */
int DoQuit()
{
int choice;
HighlightMenu(QUIT, LIGHTGRAY, BLACK);
choice = DialogBox();
HighlightMenu(QUIT, RED, WHITE);
return (choice == YES) ? QUIT : IGNORE;
}