home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Virtual Reality Zone
/
VRZONE.ISO
/
mac
/
PC
/
PCGLOVE
/
GLOVE
/
MIDIGL.ZIP
/
GLGRPH1.CPP
next >
Wrap
Text File
|
1992-10-27
|
15KB
|
580 lines
//
// GlGrph.cpp:
// Implementation of classes graphicsActor, XYZ, hand, and Rectangle.
//
// Purpose:
// Utilizing Borland's GDI toolkit to display the result,
// compute the approximate shape of the power glove in 3-D space.
//
// Author(s): Mark Pflaging C-serve 70233,1552
// Copyright 1992 Mark Thomas Pflaging
//
// Also see authorships in OOPGLOVE.DOC.
// Date: 5/15/92 - 6/4/92
//
#include "glgrph.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <mem.h>
#include <math.h>
#include "\ptk\asm\mcc.h"
char * hand::title = "Graphics";
extern irq; //interface's Irq level for midi
// extern oldx;
// extern oldy;
// extern oldz;
extern oldnote;
extern oldmidfinger;
extern num_of_notes;
extern numdiv;
extern start_note;
extern pitchbend_on;
void graphicsActor::eraseGlove()
{
// erase old box.
oldData.erase(leaveTrails);
}
// draw square cursor
void graphicsActor::drawGlove(gloveDriver &gd)
{
XYZ newRect;
newRect.scaleToGlove(gd);
// prevent redundant drawing.
if ((newRect == oldData) && (gd.getFingers() == oldFingers))
return;
if( drawn ) eraseGlove();
newRect.rectangle();
newRect.displayGlove(gd, *this);
drawn = 1;
oldData = newRect; /* save pos'n for next erase */
oldFingers = gd.getFingers();
}
//functions by mick imfeld to use xyz and index finger to control midi
// interface
int midnote;
// splits up glove x range into selected number of notes,
// starting with selected starting note
int findnote(int midxval)
{
midnote=(((midxval+127)/numdiv)+start_note);
return(midnote);
}
// this function sends the glove info to the midi interface
// using functions from the Musicquest Programmer's Toolkit
void send_midi(int midx, int midy, int midz, int midfinger)
{
int midvol_M;
int midvol_L;
int modu_vol;
int midpitch_M;
int midpitch_L;
int modu_pitch;
//assign a note number to x value
midnote=findnote(midx);
// check and see if note off, i.e. glove index finger closed
// if closed and last sample also closed, update data and return
// if closed and last sample open, send noteoff oldnote
// if open and no change of value, update data and return
// if open and change of value, send new info
if ((!midfinger) && (!oldmidfinger)) {
// oldx=midx;
// oldy=midy;
// oldz=midz;
mcc_put(0x90);
mcc_put(oldnote);
mcc_put(0);
mcc_put(0xB0);
mcc_put(0x7B);
mcc_put(0);
oldnote=midnote;
}
else if (!midfinger) {
mcc_put(0x90);
mcc_put(oldnote);
mcc_put(0);
mcc_put(0xB0);
mcc_put(0x7B);
mcc_put(0);
oldnote=midnote;
oldmidfinger=midfinger;
}
else {
if (!oldmidfinger) {
mcc_put(0x90);
mcc_put(midnote);
mcc_put(127);
}
// check to see if same note is still playing
if ((oldmidfinger) && (oldnote!=midnote)) {
mcc_put(0x90);
mcc_put(oldnote);
mcc_put(0);
mcc_put(0x90);
mcc_put(midnote);
mcc_put(127);
oldnote=midnote;
}
oldmidfinger=midfinger;
//send new pitch bend value
if (pitchbend_on) {
midpitch_M=(midy + 127) / 2;
midpitch_L=0;
modu_pitch=(midy + 127) % 2;
if (modu_pitch) midpitch_L=64;
mcc_put(0xE0);
mcc_put(midpitch_L);
mcc_put(midpitch_M);
}
//send new main volume value
midvol_M=(midz + 70);
midvol_L=0;
modu_vol=(midz + 70) % 2;
if (modu_vol) midvol_L=64;
mcc_put(0xB0);
mcc_put(7);
mcc_put(midvol_M);
mcc_put(0xB0);
mcc_put(39);
mcc_put(midvol_L);
} //end of if
} //end send_midi -back to o2glove
void XYZ::scaleToGlove( GloveData & gd )
{
setValues(
(getmaxx()/2) + 2 * gd.getX(), // compute X,Y center.
(getmaxy()/2) - 2 * gd.getY(),
30 + ( gd.getZ() ) ); // size prop. to Z.
setRectValues(x - z, y - z, x + z, y + z);
}
void XYZ::displayGlove(gloveDriver &gd, hand & myHand)
{
setThetaRad(((double)gd.getRotation() * 360.0 / 12.0) * M_PI / 180.0);
myHand.scaleRect(*this);
myHand.drawAll(gd);
}
void XYZ::rectangle()
{
setcolor( 15 ); // draw new box
::rectangle(left, top, right, bottom);
}
void XYZ::erase(int leaveTrail)
{
int erased[5 * 2] = {
right, top, right, bottom, left, bottom, left, top,
right, top
};
setfillstyle(SOLID_FILL,/*EGA_BLACK*/0);
fillpoly( 5, erased);
if (!leaveTrail) {
setcolor(0);
::rectangle(left, top, right, bottom);
}
setfillstyle(SOLID_FILL,EGA_LIGHTGRAY);
}
// (This data is not as ugly as it looks!)
// 100 x 100 is the size of the "Unit
// rectangle". The original image should
// be scaled fit in this size. See below.
int noFingers[] =
{
// Hand with no fingers. (11 coordinates)
40, 60, 40, 90, 80, 90, 80, 70, 70, 70, 70, 60,
60, 60, 60, 50, 50, 50, 50, 60
};
int thumb[] =
{
// Thumb part 1. (5 coordinates)
30, 60, 30, 80, 40, 80, 40, 60, 30, 60,
// Thumb part 2
20, 60, 20, 70, 30, 70, 30, 60, 20, 60
};
int index[] =
{
// Index part 1. (5 coordinates)
40, 40, 40, 60, 50, 60, 50, 40, 40, 40,
// Index part 2
40, 30, 40, 40, 50, 40, 50, 30, 40, 30,
// Index part 3
40, 20, 40, 30, 50, 30, 50, 20, 40, 20
};
int middle[] =
{
// For middle, same as index except add 10 to x, subtract 10 from y
// Middle part 1. (5 coordinates)
50, 30, 50, 50, 60, 50, 60, 30, 50, 30,
// Middle part 2
50, 20, 50, 30, 60, 30, 60, 20, 50, 20,
// Middle part 3
50, 10, 50, 20, 60, 20, 60, 10, 50, 10
};
int ring[] =
{
// Ring part 1. (9 coordinates)
60, 40, 60, 60, 70, 60, 70, 70, 80, 70,
80, 50, 70, 50, 70, 40, 60, 40,
// Ring part 2
60, 30, 60, 40, 70, 40, 70, 50, 80, 50,
80, 40, 80, 30, 70, 30, 60, 30,
// Ring part 3
60, 20, 60, 30, 70, 30, 70, 40, 80, 40,
80, 30, 70, 30, 70, 20, 60, 20
};
HandType handData[5] =
{
{ 1, 10, noFingers },
{ 2, 5, thumb },
{ 3, 5, index },
{ 3, 5, middle },
{ 3, 9, ring}
};
// Note that the numGroups and pointsPerGroup attributes are "static"
// and do not need to be copied for each instantiation, but the
// HandType.data should be copied since the data is modified
// before it is drawn.
//
// The only "hardwired" thing about this class is that it always
// has five "elements": the part of the hand with no fingers
// protruding, the thumb, the index finger, the middle finger, and
// the ring finger.
//
// My data was BASED on the preceding version's data, but by no
// means is it a copy. If you don't like how it looks, change it!!
// And send me your data if you please!!
//
// No need to bother to start from scratch, just copy the subarrays and
// the handData data array, rename, and you're set!
#define IntsPerPoint 2
hand::hand(InitFile & ini) : fillMode(ini.find(title, "fillMode", 0)),
original(handData)
{
for (int i = 0; i < 5; i ++)
{
int a = handData[i].numGroups;
int b = handData[i].pointsPerGroup * IntsPerPoint;
int * c = (data[i] = new int[a * b]);
// get it?
memcpy(c, handData[i].data, a * b * sizeof(int));
}
}
double Rectangle::thetaRad;
void Rectangle::setThetaRad(double value)
{
thetaRad = value;
}
// scalePoint is the heart of the graphics processing.
// It shapes the hand to fit the rectangle, and rotates
// the hand based on user input.
// This function moves the hand to the specified coordinates.
// And scales it by the specified amount. The more stuff
// we can stick into this loop, the better.
// Please note that thetaRad, the rotation angle, should already have
// been set.
void Rectangle::scalePoint(double x, double y, int & new_x, int & new_y)
{
// 100 x 100 is the size of the "Unit
// rectangle".
if (!(((int)x == 50) && ((int)y == 50))) {
// FIRST!, take care of rotation.
x -= (double)50;
y -= (double)50;
double h = sqrt(x*x+y*y), oldTheta;
if (int(x)) {
oldTheta = atan2(y, x);
}
else oldTheta = ((y > 0) ? M_PI_2 : -M_PI_2);
x = h * cos(oldTheta + thetaRad) + (double)50;
y = h * sin(oldTheta + thetaRad) + (double)50;
}
// Here is where the scaling
// itself takes place.
new_x = (int)((x * (double)(right - left)
/ (double)100.0) + (double)left);
new_y = (int)((y * (double)(bottom - top)
/ (double)100.0) + (double)top);
return;
}
void hand::scaleRect(Rectangle &scaler)
{
int tmp, k, j;
for ( int i = Fingers::NoFinger; i < (Fingers::Ring + 1); i ++)
{
int a = original[i].numGroups;
int b = original[i].pointsPerGroup * IntsPerPoint;
int * c = data[i];
int * d = original[i].data;
// get it?
for (j = 0; j < a; j ++) {
for (k = 0; k < b; k += IntsPerPoint) {
tmp = j * b + k;
scaler.scalePoint((double)d[tmp],
(double)d[tmp+1],
c[tmp], c[tmp+1]);
}
}
}
}
void hand::drawOne(Fingers::handParts index, int numParts)
{
int numPoints = original[index].pointsPerGroup;
for (int i = 0; i < numParts; i ++) {
fillpoly(numPoints, &(data[index][i * IntsPerPoint * numPoints]));
}
}
void hand::drawAll(gloveDriver & gd)
{
setfillstyle(fillMode, EGA_LIGHTGRAY);
// setcolor(EGA_LIGHTGRAY);
for (int i = Fingers::NoFinger; i < (Fingers::Ring + 1); i ++) {
drawOne((Fingers::handParts)i, gd.findNumParts((Fingers::handParts)i));
}
}
void graphicsActor::test(gloveDriver& gd)
{
// Okay, so this code is a little messy.
// I'm going to clean it up.
static textActive = 0;
static keyDown = FALSE;
int realKey;
// draw_glove_plot(gd); // plot x,y positions
// Fix problem when key is held down.
if ((realKey = gd.getKeys()) == -1) {
keyDown = FALSE;
}
else {
if (keyDown) realKey = -1;
else keyDown = TRUE;
}
// press "1" to display help...
if( (realKey == 1) && !textActive) {
cleardevice();
displayHelp();
outtext("Press the GLOVE enter key to continue.");
textActive = 1;
}
// ...and Enter to continue.
if( (realKey == -128) && textActive) {
cleardevice();
textActive = 0;
}
// press "2" to toggle drawing of moving hand.
if( (realKey == 2)) {
gloveActive = !(gloveActive);
if (!gloveActive) eraseGlove();
}
// press "3" to toggle drawing of nonmoving hand.
if( (realKey == 3)) {
handActive = !(handActive);
if (!handActive) eraseFingers();
}
// press "4" to toggle info.
if( (realKey == 4)) {
infoActive = !(infoActive);
if (!infoActive) infoErase(0);
}
// press "A" to toggle trails.
if( (realKey == 10)) {
leaveTrails = !(leaveTrails);
}
// press "B" to clear screen.
if( (gd.getKeys() == 11)) {
cleardevice();
}
if (!textActive) {
if (gloveActive)
drawGlove(gd); // animate glove cursor
if (handActive)
drawFingers(gd); // draw the nonmoving "hand"
if (infoActive)
infoDisplay(gd);
// gesture.sense(gd); // wishful thinking.
// gesture.display();
}
}
void graphicsActor::displayHelp()
{
cleardevice();
// output a message
int x = getmaxx() / 2;
int y = 5; // getmaxy() / 2;
settextjustify(CENTER_TEXT, CENTER_TEXT);
int height = textheight("Tq");
moveto(x,y+=height);
outtext("This program tests the Nintendo Power Glove");
moveto(x,y+=height);
outtext("when connected to an IBM PC parallel port.");
moveto(x,y+=height);
moveto(x-=(getmaxx()/4),y+=height);
settextjustify(LEFT_TEXT, TOP_TEXT);
moveto(x,y+=height);
outtext("The following POWER GLOVE keys (NOT keyboard keys!)");
moveto(x,y+=height);
outtext("have been defined:");
moveto(x,y+=height);
outtext("1 - display this help screen");
moveto(x,y+=height);
outtext("2 - toggle drawing of moving hand.");
moveto(x,y+=height);
outtext("3 - toggle drawing of nonmoving hand.");
moveto(x,y+=height);
outtext("4 - to toggle info display.");
moveto(x,y+=height);
moveto(x,y+=height);
outtext("A - to toggle \"trails\" behind the rectangle (try it!)");
moveto(x,y+=height);
outtext("B - to clear screen.");
moveto(x,y+=height);
outtext("Press any KEYBOARD key to exit the program.");
moveto(x,y+=height);
moveto(x,y+=height);
}
#define LINES 4
#define XOFF 20
#define YOFF 25
#define NUMSTART 17
void graphicsActor::infoErase(int start)
{
int unit_wid = textwidth("W");
int x = XOFF + unit_wid * start, y = YOFF;
static char hightxt[] = "Tq";
int height = textheight(hightxt);
int width = unit_wid * (33 - start);
setviewport(x, y, width + x, height * LINES + y, 1);
clearviewport();
setviewport(0, 0, getmaxx(), getmaxy(), 0);
}
void graphicsActor::infoDisplay(gloveDriver & gd)
{
char temp[255];
int height = textheight("Tq");
int width = textwidth("W");
infoErase(NUMSTART);
int x = XOFF, y = YOFF;
setcolor(EGA_LIGHTGRAY);
// print constant text:
moveto(x + width * 8, y);
outtext("X, Y, Z:");
// print rot, fingers, keys
moveto(x + width * NUMSTART, y);
sprintf(temp, "%4d, %4d, %4d",
gd.getX(), gd.getY(), gd.getZ());
outtext(temp);
moveto(x,y+=height);
outtext(" Rotation, Keys:");
sprintf(temp, " %2d, %-2x %-3d",
gd.getRotation(), gd.getKeys(), gd.getKeys());
moveto(x + width * NUMSTART, y);
outtext(temp);
moveto(x + width * 8, y += height);
outtext("Fingers:");
sprintf(temp, " %2x, %2x, %2x, %2x",
gd.findNumParts(Fingers::Thumb),
gd.findNumParts(Fingers::Index),
gd.findNumParts(Fingers::Middle),
gd.findNumParts(Fingers::Ring));
moveto(x + width * NUMSTART, y);
outtext(temp);
//Here the midi send function is called by Mick Imfeld
send_midi(gd.getX(), gd.getY(), gd.getZ(), gd.findNumParts(Fingers::Index));
// return to o2glove
}
graphicsActor::graphicsActor(InitFile & ini) : hand(ini), drawn(0), xx(0),
gloveActive(ini.find(title, "gloveActive", 1)),
handActive(ini.find(title, "handActive", 1)),
infoActive(ini.find(title, "infoActive", 1)),
leaveTrails(ini.find(title, "leaveTrails", 0))
{
int gdriver = DETECT, gmode, errorcode;
// detect graphics hardware available
detectgraph(&gdriver, &gmode);
// gdriver now contains detected hardware info.
// (see graphics_drivers enum type in help)
initgraph(&gdriver, &gmode, NULL); // detect best graphics mode
// read result of initialization
errorcode = graphresult();
if (errorcode != grOk) // an error occurred
{
printf("Graphics error: %s\n", grapherrormsg(errorcode));
printf("Press any key to halt:");
getch();
exit(1); // exit with error code
}
// Ah, hah, hah, hah, hah, hah, hah, HAH!
// outtext("Press 1, 2, or 3 to select a parallel port.");
}