Bilag L: Koden til Det virtuelle Bibliotek

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "wt.h"	
	  
#define MIN 0
#define MAX 1
#define TXTWIDTH 512
#define TXTHEIGHT 1024
#define RECORD_SIZE 1743

long antal = 4815;
double factor = 0.5;
double x_min, x_max, y_min, y_max, z_min, z_max;
double x_min_old, x_max_old, y_min_old, y_max_old, z_min_old, z_max_old;
char x_min_text[100], x_max_text[100], y_min_text[100], y_max_text[100], z_min_text[100], z_max_text[100];

int var_len[11] = {12,75,500,500,50,75,15,10,2,3,100};
int var_pos[11] = {0,12,87,587,1087,1137,1212,1227,1237,1239,1242};


WTpq vp;
WTviewpoint *uview;
WTp3 vppos, center;
Wtobject	*room, *probe, *temp_object, *temp_object2, *temp2_object,
		 	*shadow_xy1, *shadow_xy2, *shadow_xz1, *shadow_xz2,
		 	*shadow_yz1, *shadow_yz2,
		 	*label_x, *label_y, *label_z,
		 	*wall_xy, *wall_xz, *wall_yz;


Wtpoly	*xy1, *xy2, *xz1, *xz2, *yz1, *yz2,
			*wall_xy_front, *wall_xz_front, *wall_yz_front;

WTpq trip_location;
WTpathnode *trip_node;


WTq z_plus, z_minus, y_plus, y_minus, x_plus, x_minus, vpori, xy_ori, xz_ori, yz_ori, trip_ori;
int pickmode = FALSE;
int pickedprobe = FALSE;
int point = FALSE;
int swarm = TRUE;
int wall = FALSE;
int automode = TRUE;
int color_mode = FALSE;
int titles_on = FALSE;
int short_titles_on = FALSE;
int thin_objects_on = TRUE;
int textures_on = TRUE;
int text_info = TRUE;
int distant_title = FALSE;
int highlight = FALSE;
int j;

long dummya = 'a', dummyb = 'b', dummyc = 'c', dummyp = -1, dummyx = -2; dummyh = -3;
// p for permanent, x for erasable, h for highlight
long dummies[4815];

long *objecttype ;

char new_bruns_wick[100] = "c:\\inforoom\\textures\\newbruns\\";
char avalon[100] = "c:\\inforoom\\textures\\avalon\\";
char poerfie[100] = "c:\\inforoom\\textures\\poerfie\\";
char gensing[100] = "c:\\inforoom\\textures\\gensing\\";

enum var
	{
	id,
	author,
	title,
	subtitle,
	published_in,
	publisher,
	year,
	size,
	type,
	base,
	keywords
	};

enum var x_var, y_var, z_var;

// Text for axis labels
char id_text[15] = "Id number";
char author_text[15] = "Author";
char title_text[15] = "Title";
char subtitle_text[15] = "Subtitle";
char published_in_text[15] = "Published in";
char publisher_text[15] = "Publisher";
char year_text[15] = "Year";
char size_text[15] = "Size";
char type_text[15] = "Type";
char base_text[15] = "Base";
char keywords_text[15] = "Keywords";
char *axis_texts[11] = {&id_text, &author_text, &title_text, &subtitle_text, &published_in_text,
&publisher_text, &year_text, &size_text, &type_text, &base_text, &keywords_text};


// text for book information
char type1_text[20] = "Trykt tekst";
char type2_text[20] = "Mikrokort";
char type3_text[20] = "CD-rom";
char type4_text[20] = "AV-materiale";

char base1_text[20] = "Emneord";
char base2_text[20] = "Monografi";
char base3_text[20] = "Periodica";
char base4_text[20] = "Artikel";
char base5_text[20] = "┼rsberetning";
char base6_text[20] = "AV";
char base7_text[20] = "Bibliografi";
char base8_text[20] = "Forskningsprojekt";
char base9_text[20] = "Indlsn";

char *type_texts[26] = {&type1_text, &type2_text, &type3_text,0,0,0,0,0,0,0,0,0,0,0,0,0,&type4_text};
char *base_texts[27] = {0,0,&base1_text,&base2_text,&base3_text,&base4_text,&base5_text,
						&base6_text,0,0,&base7_text,0,0,0,0,0,0,0,0,0,&base8_text,0,0,0,0,0,&base9_text};

char x_axis_text[30], y_axis_text[30], z_axis_text[30];

WTsensor *sensor1, *sensor2, *sensor3;	
WTfont3d *font;	  /* Font file		      */

// header for TARGA-file
struct imageheader {
	unsigned char idlength;
	unsigned char comaptype;
	unsigned char imgtype;
	unsigned char index_lo,index_hi;
	unsigned char length_lo,length_hi;
	unsigned char cosize;
	unsigned char x_org_lo,x_org_hi;
	unsigned char y_org_lo,y_org_hi;
	unsigned char width_lo,width_hi;
	unsigned char height_lo,height_hi;
	unsigned char pixelsize;
	unsigned char attbits;
	//unsigned char rsrvd;
	//unsigned char orgbit;
	//unsigned char intrlve;
	};

// arrays for holding text textures
unsigned _int16 texture[TXTWIDTH][TXTHEIGHT];
unsigned _int16 book_texture[TXTWIDTH][TXTHEIGHT];

// arrays for holding point mode teaxtures
_int16 xy[128][128];
_int16 xz[128][128];
_int16 yz[128][128];


// arrays for holding color mode data
_int16 color_xy[10][10];
_int16 color_xz[10][10];
_int16 color_yz[10][10];

int pos_z, pos_x, pos_y;

int file_counter = 0;

int rhymes = FALSE;
int syretrip = FALSE;

/* The function clean_string removes all " and / characters
from input string. This is done to prevent problems when using
the input string as a filename.*/

void clean_string(char *string_in, char *string_out)
{
	while (*string_in != 0)
	{
		*string_out = *string_in;
		switch (*string_in)
		{
			case 47: *string_out = '-'; break;
			case 34: *string_out = '-'; break;
		}
		string_in++;
		string_out++;
	}
	*string_out = 0;
}

/*The function trunc_string truncates the input string to a
length of len characters.*/

void trunc_string (char *string, int len)
{
	string[len] = 0;
}
/*The function c2c converts the input character to uppercase
if it is lowercase.*/

char c2c (char ch) // convert to caps
{
	if ((ch > 96) && (ch < 123))
		return ch-32;
	else
		return ch;
}

/* The function greater_than compares the two strings
first and second. It returns TRUE if first is greater than second, otherwise FALSE. If one of the strings is '˝', that string is automatically considered the greatest. An empty string is considered the smallest. */

int greater_than (char *first, char *second) // first > second ?
{
	if ( *second == '˝') return FALSE;
	if ( *first == '˝') return TRUE;
	if ( *first == '\0') return FALSE;
	if ( *second == '\0') return TRUE;
	while ((c2c(*first) == c2c(*second)) && (*first != '\0')) // equal and none \0
	{
		first++;
		second++;
	}
	if ((unsigned char)c2c(*first) > (unsigned char)c2c(*second))
		return TRUE;
	else
		return FALSE;
}
/* The function greater_than_or_equal_to compares the two
strings first and second. It returns TRUE if first is greater
than or equal to the second, otherwise FALSE. If one of the
strings is '˝', that string is automatically considered the
greatest. An empty string is considered the smallest. */

int greater_than_or_equal_to (char *first, char *second) // first >= second ?
{
	if ( *second == '˝') return FALSE;
	if ( *first == '˝') return TRUE;
	if ( *first == '\0') return FALSE;
	if ( *second == '\0') return TRUE;
	while ((c2c(*first) == c2c(*second)) && (*first != '\0')) // equal and none \0
	{
		first++;
		second++;
	}
	if ((*first == '\0') && (*second == '\0')) return TRUE;
	if ((unsigned char)c2c(*first) > (unsigned char)c2c(*second))
		return TRUE;
	else
		return FALSE;
}

/* The function search takes as input the variable to make the
search for, the upper and lower boundaries, and the name of the
file in which to put the result of the search.
First the indexfile for the chosen variable is determined. If
the chosen variable is 'keywords', five indexfiles (one for
each keyword) are accessed and five output files created.
The function then performs a binary search on the indexfile(s)
and locates the first and last book to be included in the result.
When these two books are located, the numbers of these and those
in between are written in the output file. */

void search (int var, char *output_filename, char *lower_string, char *upper_string)
{
	long lower_boundary, upper_boundary, left, right, middle, k;
	int found_lower, found_upper;
	int rec_size;
	char test_string[1000];
	char pos_string[15];
 	char input[15], output[15];
	char indexno[3];
                   
	int in, out;
	int passes, n;                   
 	char index_ch[2] = {'\0', '\0'};

	rec_size = var_len[var]+15;

	strcpy(output, output_filename);

	_itoa(var, indexno, 10);
	strcpy(input, "INDEX");
	strcat(input, indexno);
			

 	if (var == keywords)
		{
			passes = 5;
			strcat(input, "x");
			strcat(output, "x");
		}
	else
		passes = 1;    
    
	for (n=0; n= atoi(lower_string))
					right = middle;
				else
					left = middle;
			}
			else
			{

				if (greater_than_or_equal_to (test_string, lower_string))
					right = middle;
				else
					left = middle;
			}
			if ( (right-left) == 1)
			{
				found_lower = TRUE;
				if (left == 0)
				{
					_lseek (in, 0, SEEK_SET);
					_read ( in, test_string, var_len[var]);
					if (var == size)
					{
						if (atoi(lower_string) > atoi(test_string))
							lower_boundary = right;
						else
							lower_boundary = left;
					}
					else
					{
						if (greater_than (lower_string,test_string))
							lower_boundary = right;
						else
							lower_boundary = left;
					}
				}
				else
					lower_boundary = right;
			}
		}
		while (!found_lower);                          

		// find upper boundary (binary search)             
		found_upper = FALSE;
		left = 0;
		right = antal-1;
		do
		{                    
			middle = (left+right) / 2;
			_lseek (in, 0+middle*rec_size, SEEK_SET);
			_read (in, test_string, var_len[var]);
			if (var == size)
			{
				if (atoi(test_string) > atoi(upper_string))
					right = middle;
				else
					left = middle;
			}
			else
			{
				if (greater_than (test_string, upper_string))
					right = middle;
				else
					left = middle;
			}
			if ( (right-left) == 1)
			{
				found_upper = TRUE;
				if (left == (antal-1))
				{
					_lseek (in, 0+(antal-1)*rec_size, SEEK_SET);
					_read (in, test_string, var_len[var]);
					if (var == size)
					{
						if (atoi(test_string) > atoi(upper_string))
							lower_boundary = left;
						else
							lower_boundary = right;
					}
					else
					{
						if (greater_than (test_string,upper_string))
							upper_boundary = left;
						else
							upper_boundary = right;
					}
				}
				else
					upper_boundary = left;
			}
		}
		while (!found_upper);

		// copy all selected records
	
		for (k=lower_boundary; k<(upper_boundary+1); k++)
		{
			_lseek (in, var_len[var]+k*rec_size, SEEK_SET);
			_read (in, pos_string, 15);
			_write ( out, pos_string, 15);
			//fputc ('\n', out);
		}
		pos_string[0] = 0;
		_write(out, pos_string, 1); // one more byte for the road (prevents premature eof)
		_close (in);
		_close (out);
	}
}
/* The function tag_file is called by merge_searchfiles. It 
takes as input a search file created by the function search. For
each book in this file, it marks its occourence in a tagfile
(for more details see merge_searchfiles). */

void tag_file(int file, int tf, int bit_number, int add) //tag all records found in 'file'
{
	char index_string[15];
	char ch;
	
	while (_eof(file) == 0)
	{
		_read (file, index_string, 15);
		if (_eof(file) == 0)
		{
			// mark first bit with 1, 2, or 4
			_lseek (tf, atol(index_string)*4, SEEK_SET);
			_read (tf, &ch, 1);
			ch = ch | (1 << (bit_number-1));
			_lseek (tf, atol(index_string)*4, SEEK_SET);
			_write (tf, &ch, 1);
			
			// add #add to bit number 1, 2 or 3
			_lseek (tf, atol(index_string)*4+bit_number, SEEK_SET);
			_read (tf, &ch, 1);
			ch = ch | add;
			_lseek (tf, atol(index_string)*4+bit_number, SEEK_SET);
			_write (tf, &ch, 1);

		}
	}
}


/* The function merge_searchfiles takes as input three (or, if
some of the variables are 'keywords', five for each 'keywords'
variable) search files created by the function search.
For each input file the function tag_file is called. The
resulting tagfile contains four bytes for each book in the database.
The first byte tells in how many of the search files, the book
occoured (one bit for each variable); if this byte is seven
(binary 111) the book was included in all search files.
The following three bytes are only used if one or more of the
vaiables is 'keywords'. These bytes indicate how many of the
book's keywords were within the boundaries of the search. */

void merge_searchfiles (char *file1, char *file2, char *file3) // merge 3 searchfiles to one tagfile
{
	
	int f1, f2, f3, tagfile;
	int add;
	long k;
	char ch;
	char filename_copy[15];

	// create tagfile with four bytes for each book in the database
	tagfile = _open( "tagfile", _O_RDWR );
	ch = '\0';
	for (k=0; k<(antal*4); k++) _write (tagfile, &ch, 1);
    
	// tag file(s) for x variable
	if (x_var == keywords)
		{
			strcpy(filename_copy, file1);
			strcat(filename_copy, "x");
			for (k=0; k<5; k++)
				{
					ch = k+97;
					filename_copy[strlen(filename_copy)-1] = ch;
					filename_copy[strlen(filename_copy)] = '\0';
					f1 = _open( filename_copy, _O_RDONLY  );
					tag_file (f1, tagfile, 1, 1 << k);
					_close (f1);
				}
		}
	else
		{
			f1 = _open( file1, _O_RDONLY  );
			tag_file (f1, tagfile, 1, 0);
			_close (f1);
		}
	
	// tag file(s) for y variable
	if (y_var == keywords)
		{
			strcpy(filename_copy, file2);
			strcat(filename_copy, "x");
			for (k=0; k<5; k++)
				{
					ch = k+97;
					filename_copy[strlen(filename_copy)-1] = ch;
					filename_copy[strlen(filename_copy)] = '\0';
					f2 = _open( filename_copy, _O_RDONLY  );
					tag_file (f2, tagfile, 2, 1 << k);
					_close (f2);
				}
		}
	else
		{
			f2 = _open( file2, _O_RDONLY  );
			tag_file (f2, tagfile, 2, 0);
			_close (f2);
		}
		
	
	
	// tag file(s) for z variable
	if (z_var == keywords)
		{
			strcpy(filename_copy, file3);
			strcat(filename_copy, "x");
			for (k=0; k<5; k++)
				{
					ch = k+97;
					filename_copy[strlen(filename_copy)-1] = ch;
					filename_copy[strlen(filename_copy)] = '\0';
					f3 = _open( filename_copy, _O_RDONLY  );
					tag_file (f3, tagfile, 3, 1 << k);
					_close (f3);
				}
		}
	else
		{
			f3 = _open( file3, _O_RDONLY  );
			tag_file (f3, tagfile, 3, 0);
			_close (f3);
		}
		
	
	
	//create tagfile
	
    //tag_file (f1, tagfile,1,0);
    //tag_file (f2, tagfile,2,0);
	//tag_file (f3, tagfile);    
        
	//_close (f1);
	//_close (f2);
	//_close (f3);
	_close (tagfile);
}


/* user action function  */
static void actionfn();

/* converts 3 numbers (x,y,z) to a position p (array of float)*/

void convert_xyz2pos(float x, float y, float z, float *p)
{
	p[X] = x;
	p[Y] = y;
	p[Z] = z;
}

/* converts 4 numbers (x,y,z,w) to an orientation q (array of float)*/

void convert_xyzw2ori(float x, float y, float z, float w, float *q)
{
	q[X] = x;
	q[Y] = y;
	q[Z] = z;
	q[W] = w;
}


/* converts 3 numbers (x,y,z) to an orientation q (array of float)*/

void convert_xyz2ori(float x, float y, float z, float *q)
{
	WTp3 d;

	d[X] = x;
	d[Y] = y;
	d[Z] = z;
		
	WTdir_2q(d,q);

}

/* Sets the position (x,y,z) of an object. More convenient than
than WTobject_setposition, which takes a WTp3 (array of float)
as input. */

void object_setposXYZ (WTobject *object, float x, float y, float z)
{
	WTp3	temppos;
	
	convert_xyz2pos(x,y,z,temppos);
	WTobject_setposition(object,temppos);
}

/* Sets the orientation (x,y,z) of an object. More convenient than
than WTobject_setposition, which takes a WTq (array of float)
as input. */

void object_setoriXYZ (WTobject *object, float x, float y, float z)
{
	WTq	tempori;
	
	convert_xyz2ori(x,y,z,tempori);
	WTobject_setorientation(object,tempori);
}
	
	
/* Returns the x coordinate of the probe. */

float probeX()
{
	WTp3 p;
	WTobject_getposition(probe,p);
	return p[X];
}

/* Returns the y coordinate of the probe. */

float probeY()
{
	WTp3 p;
	WTobject_getposition(probe,p);
	return p[Y];
}

/* Returns the z coordinate of the probe. */

float probeZ()
{
	WTp3 p;
	WTobject_getposition(probe,p);
	return p[Z];
}

/* Returns the low byte of the input integer. */

int byte_low(int input)
{	
	int temp;

	if (input >= 0)
		{
			temp = input%256;
			return temp;
		}
	else
			return 0;

}

/* Returns the high byte of the input integer. */

int byte_high(int input)
{
	int temp;
	if (input >= 0)
		{
			temp = input/256;
			return temp;
		}
	else
		return 0;
}


/* Initializes the array texture with bgcolor. */

void initialize_array(int bgcolor)
{
	int i, ii;

	for (i=0; i= TXTWIDTH)
						{
						max_width = TRUE;
						break;
						}
					else texture[array_curpos_hori+iii][array_curpos_vert+ii] = pixelcolor;
				}
				if (pixelsize == 24) 
				{
					_read( fh, &byte1, sizeof(byte1));
					_read( fh, &byte2, sizeof(byte2));
					_read( fh, &byte3, sizeof(byte3));
					if ((byte1==0) && (byte2==0) && (byte3==0))
						pixelcolor = fgcolor;
					else
						pixelcolor = bgcolor;
					if (array_curpos_hori+iii >= TXTWIDTH) 
						{
						max_width = TRUE;
						break;
						}
					else texture[array_curpos_hori+iii][array_curpos_vert+ii] = pixelcolor;
				}
				if (pixelsize == 8) 
				{
					_read( fh, &byte1, sizeof(byte1));
					if (byte1 == 0)
						pixelcolor = fgcolor;
					else
						pixelcolor = bgcolor;
					if (array_curpos_hori+iii >= TXTWIDTH) 
						{
						max_width = TRUE;
						break;
						}
					else texture[array_curpos_hori+iii][array_curpos_vert+ii] = pixelcolor;
				}
 	 		}

		 
	    // close file
	    _close( fh );	 	
		
		array_curpos_hori += charwidth;
		totalwidth += charwidth;
		if (!firstcharacter)
			totalwidth += spacing;

		firstcharacter = 0;
			}		
	}
	if (max_width) totalwidth = TXTWIDTH;
	aspect_ratio = (totalwidth/totalheight);

    save_texturefile(texture, filename, TXTWIDTH, TXTHEIGHT, totalwidth, totalheight);

	return aspect_ratio;

} 

/* The function texture_textobject creates a new object with the
textstring text added as a texture on one side. The texture is created
with a call to convert_string2texture and is then added to a block
created by WTobject_newblock. */

WTobject *texture_textobject(char text[30], float texture_height, char font_dir[100], int spacing)
{
	WTobject *texture_object;
	WTpoly *texture_fors;
	float aspect_ratio, texture_width;
	char texture_filename[100];
	_int16 bgcolor = 0;
	int delete;


	new_temp_filename(texture_filename);
	
	aspect_ratio = convert_string2texture( text, texture_filename, font_dir, spacing, 0x7fff, bgcolor);

	if (aspect_ratio>0)
	{
		texture_width = aspect_ratio * texture_height;
		texture_object = (WTobject_newblock (texture_width, texture_height, 1, TRUE, FALSE));

		texture_fors = WTobject_getpolys(texture_object);
		texture_fors = WTpoly_next(texture_fors);
		texture_fors = WTpoly_next(texture_fors);
		texture_fors = WTpoly_next(texture_fors);

		WTobject_settexture(texture_object, "c:\\inforoom\\textures\\black1.tga", FALSE, TRUE);
		WTpoly_settexture(texture_fors, texture_filename, FALSE, TRUE);
		WTobject_rotate(texture_object,Z,3.14,WTFRAME_WORLD);
		
		return texture_object;
	}

}

/* Converts a string to a decimal number. The string is treated
as a base 256 number, where the first character represents 1's,
the next 1/256's, 1/(256*256)'s etc. The purpose of the function
is to convert data about a book into coordinates. */

double convert_string2number(char *string)
{
	long k;
	double return_value;
	int len = strlen (string);
	unsigned char charpos;

	return_value = 0;
	for (k=0; k= 97) && (charpos <= 122)) //convert to caps
			charpos = string[k] - 32;
					
		return_value +=  (double)exp((double)log(charpos) + (-(double)k*(double)log(256)));

	} 
	return return_value;
}

/* Converts a number to a string (the reverse of convert_string2number).
The number is in fact converted to a base 256 number. The integer part
is treated as the ascii code for the first character, and the fraction
part is the multiplied by 256, whereafter the proces is repeated until
the desired precision is reached.*/

void convert_number2string(double value, char *string)
{
	long k;
	double return_value;
	int len = 5; // max len of returned string
	unsigned char charpos;

	for (k=0; k= 30) return 15*64;
	return ((n+2)/2)*64;
}

/* The function paint_room creates all objects in the room (except the
probe, which is permanent), including 6 axes (with labels) and objects for
holding the textures on the walls in colormode and pointmode. It also
creates the colormode and pointmode textures.
First the axes are created. Then the boundaries are calculated, and the
total number of objects to be included in the room is determined. After this
the main loop begins; for each object in the database, which is tobe included,
one object is created if in swarm mode, three if in wall mode, and none if in
point mode or color mode (if one or more of the variables is 'keywords' the
same database objec may be represented more than once). If in point or color mode
instead of creating an object, a number is added to the point or color arrays.
When the loop is finished, the resulting textures (for point or color mode) are
added to the walls. */

void paint_room (double data_size)
{
	int tagfile, database;
	long k;
	char ch, x_ch, y_ch, z_ch;
	WTp3 p;
	char string1[500];
	char string2[500];
	char string3[500];
	char type_string[2];
	char base_string[3];

	double x, y, z;
	int x_passes, y_passes, z_passes, x_counter, y_counter, z_counter;
	int i,ii, i2, ii2, temp;
	double wallsize = 128.0;
	int axis_margin;
	float label_offset;
	int counter=0;
	int counter1=0;
	char mediatype = 'b';

	WTobject *x_axis_xz, *x_axis_xy, *y_axis_xy, *y_axis_yz, *z_axis_xz, *z_axis_yz;
	WTpoly *temp_poly;
	char temp_string[100];

	printf("\nUpdating room, please wait...\n");

	// setup axis'
	axis_margin = 500.0*data_size*1.1;
	label_offset = 100.0*data_size;
	create_axis( poerfie, x_var, x_min_text, x_max_text,X,0,-499,axis_margin, y_minus, Y, 3.14, Y, 3.14, Z,
	label_offset, data_size);
	create_axis( poerfie, x_var, x_min_text, x_max_text,X,0,-axis_margin,499, z_minus, X, 3.14, X, 3.14, Y,
	-label_offset, data_size);
	create_axis( poerfie, y_var, y_min_text, y_max_text,Y,-axis_margin,0,499, z_plus, Z, 3.14/2, Z, 3.14/2,
	X, -label_offset, data_size);
	create_axis( poerfie, y_var, y_min_text, y_max_text,Y,-499,0,axis_margin, x_minus, X, 3.14/2, X, 3.14/2,
	Z, label_offset, data_size);
	create_axis( poerfie, z_var, z_min_text, z_max_text,Z,-axis_margin,-499,0, y_minus, Y, 3.14/2, Y, 3.14/2,
	X, -label_offset, data_size);
	create_axis( poerfie, z_var, z_min_text, z_max_text,Z,-499,-axis_margin,0, x_minus, Y, 0, X, 3.14, Y,
	-label_offset, data_size);

	//open database files
  	tagfile = _open( "tagfile", _O_RDWR );
	database = _open( "library", _O_RDONLY);

	//count objects to be included
	for (i=0; i=0)&&(pos_y>=0)&&(pos_z>=0))
								{
								xy[pos_x][pos_y] = 0x7fff;
								xz[pos_x][pos_z] = 0x7fff;
								yz[pos_y][pos_z] = 0x7fff;
								}
						}

						//color mode
 						if (color_mode)
						{
							pos_x = ((x-x_min)/(x_max-x_min))*9.9;
							pos_y = ((y-y_min)/(y_max-y_min))*9.9;
 							pos_z = 9.9-(((z-z_min)/(z_max-z_min))*9.9);
				

							if ((pos_x<=9)&&(pos_y<=9)&&(pos_z<=9)&&
								(pos_x>=0)&&(pos_y>=0)&&(pos_z>=0))
								{
								color_xy[pos_x][pos_y] += 1;
								color_xz[pos_x][pos_z] += 1;
								color_yz[pos_y][pos_z] += 1;
								}
						}


					} // if
					} // for
				} // for
			} // for



		}

	
	}
	
	//point mode
	if (point)
		{
		// Create 3 wall objects for holding pointmode textures
		wall_xy = (WTobject_newblock(1000*factor, 1000*factor, 0.1, FALSE, FALSE));
		objecttype = &dummyx;
		WTobject_setdata(wall_xy, (void*)objecttype);
		object_setposXYZ(wall_xy, 0, 0, 499);
		WTobject_setorientation(wall_xy,z_plus);
		WTobject_rotate(wall_xy,Z,3.14,WTFRAME_WORLD);
		WTobject_settexture(wall_xy, "c:\\inforoom\\textures\\black1", FALSE, TRUE);
		wall_xy_front = WTobject_getpolys(wall_xy);
		for (j=0; j<3; j++) wall_xy_front = WTpoly_next(wall_xy_front);
	
		wall_xz = (WTobject_newblock(1000*factor, 1000*factor, 0.1, FALSE, FALSE));
		objecttype = &dummyx;
		WTobject_setdata(wall_xz, (void*)objecttype);
		object_setposXYZ(wall_xz, 0, -499, 0);
		WTobject_setorientation(wall_xz,y_plus);
		WTobject_settexture(wall_xz, "c:\\inforoom\\textures\\black1", FALSE, TRUE);
		wall_xz_front = WTobject_getpolys(wall_xz);
		for (j=0; j<5; j++) wall_xz_front = WTpoly_next(wall_xz_front);
	
		wall_yz = (WTobject_newblock(1000*factor, 1000*factor, 0.1, FALSE, FALSE));
		objecttype = &dummyx;
		WTobject_setdata(wall_yz, (void*)objecttype);
		object_setposXYZ(wall_yz, -499, 0, 0);
		WTobject_setorientation(wall_yz,x_plus);
		WTobject_rotate(wall_yz,X,-3.14159/2,WTFRAME_WORLD);
		WTobject_settexture(wall_yz, "c:\\inforoom\\textures\\black1", FALSE, TRUE);
		wall_yz_front = WTobject_getpolys(wall_yz);
		for (j=0; j<5; j++) wall_yz_front = WTpoly_next(wall_yz_front);
	
		//create textures
		new_temp_filename(temp_string);
		save_texturefile(xy,temp_string , wallsize, wallsize, wallsize, wallsize);
		WTpoly_settexture(wall_xy_front, temp_string, FALSE, TRUE);
		WTpoly_mirrortexture(wall_xy_front); 
		
		new_temp_filename(temp_string);
		save_texturefile(xz,temp_string , wallsize, wallsize, wallsize, wallsize);
		WTpoly_settexture(wall_xz_front, temp_string, FALSE, TRUE);
		WTpoly_mirrortexture(wall_xz_front);
		
		new_temp_filename(temp_string);
		save_texturefile(yz,temp_string , wallsize, wallsize, wallsize, wallsize);
		WTpoly_settexture(wall_yz_front, temp_string, FALSE, TRUE);
		}
	//color mode
	if (color_mode)
	{
		for (i=0; i<10; i++) // calculate colors
			for (ii=0; ii<10; ii++)
			{	
				color_xy[i][ii] = calculate_color(color_xy[i][ii]);
				color_xz[i][ii] = calculate_color(color_xz[i][ii]);
				color_yz[i][ii] = calculate_color(color_yz[i][ii]);
			}
		for (i=0; i<10; i++) // blow up bitmap
			for (ii=0; ii<10; ii++)
				for (i2=0; i2<10; i2++) 
					for (ii2=0; ii2<10; ii2++)
					{
						xy[i*10+i2][ii*10+ii2] = color_xy[i][ii];
						xz[i*10+i2][ii*10+ii2] = color_xz[i][ii];
						yz[i*10+i2][ii*10+ii2] = color_yz[i][ii];
					}
		// Create 3 wall objects for holding colormode textures
		wall_xy = (WTobject_newblock(1000*factor, 1000*factor, 0.1, FALSE, FALSE));
		objecttype = &dummyx;
		WTobject_setdata(wall_xy, (void*)objecttype);
		object_setposXYZ(wall_xy, 0, 0, 499);
		WTobject_setorientation(wall_xy,z_plus);
		WTobject_rotate(wall_xy,Z,3.14,WTFRAME_WORLD);
		WTobject_settexture(wall_xy, "c:\\inforoom\\textures\\black1", FALSE, TRUE);
		wall_xy_front = WTobject_getpolys(wall_xy);
		for (j=0; j<3; j++) wall_xy_front = WTpoly_next(wall_xy_front);
	
		wall_xz = (WTobject_newblock(1000*factor, 1000*factor, 0.1, FALSE, FALSE));
		objecttype = &dummyx;
		WTobject_setdata(wall_xz, (void*)objecttype);
		object_setposXYZ(wall_xz, 0, -499, 0);
		WTobject_setorientation(wall_xz,y_plus);
		WTobject_settexture(wall_xz, "c:\\inforoom\\textures\\black1", FALSE, TRUE);
		wall_xz_front = WTobject_getpolys(wall_xz);
		for (j=0; j<5; j++) wall_xz_front = WTpoly_next(wall_xz_front);
	
		wall_yz = (WTobject_newblock(1000*factor, 1000*factor, 0.1, FALSE, FALSE));
		objecttype = &dummyx;
		WTobject_setdata(wall_yz, (void*)objecttype);
		object_setposXYZ(wall_yz, -499, 0, 0);
		WTobject_setorientation(wall_yz,x_plus);
		WTobject_rotate(wall_yz,X,-3.14159/2,WTFRAME_WORLD);
		WTobject_settexture(wall_yz, "c:\\inforoom\\textures\\black1", FALSE, TRUE);
		wall_yz_front = WTobject_getpolys(wall_yz);
		for (j=0; j<5; j++) wall_yz_front = WTpoly_next(wall_yz_front);
	
		//create color textures
		new_temp_filename(temp_string);
		save_texturefile(xy,temp_string , wallsize, wallsize, 100, 100);
		WTpoly_settexture(wall_xy_front, temp_string, FALSE, TRUE);
		WTpoly_mirrortexture(wall_xy_front); 
		
		new_temp_filename(temp_string);
		save_texturefile(xz,temp_string , wallsize, wallsize, 100, 100);
		WTpoly_settexture(wall_xz_front, temp_string, FALSE, TRUE);
		WTpoly_mirrortexture(wall_xz_front);
		
		new_temp_filename(temp_string);
		save_texturefile(yz,temp_string , wallsize, wallsize, 100, 100);
		WTpoly_settexture(wall_yz_front, temp_string, FALSE, TRUE);
	}
	close(tagfile);
	close(database);
	printf("\nDone, updating room!\n");
}

/* This function deletes all objects in the room, except for the probe, which
is permanent. After this, paint_room is called. */

void redraw_room()
	{
		temp_object = WTuniverse_getobjects(); // first object in list
		while (temp_object != NULL) // delete all non-permanent objects
		{
			temp_object2 = temp_object;
			temp_object = WTobject_next(temp_object);
		
			objecttype = (long*)WTobject_getdata(temp_object2);
			if (*objecttype != -1) WTobject_delete(temp_object2);
		}
			
		paint_room (factor);
	}

/* This function deletes all highlight objects in the room. The highlights show
which object are currently within the probe in swarm mode. */

void delete_highlights()
	{
		temp_object = WTuniverse_getobjects(); // first object in list
		while (temp_object != NULL) // delete existing boundary boxes (highlight objects)
		{
			temp_object2 = temp_object;
			temp_object = WTobject_next(temp_object);
		
			objecttype = (long*)WTobject_getdata(temp_object2);
			if (*objecttype == -3) WTobject_delete(temp_object2);
		}
	}

/* This function searches the object list and creates highlight object for each
object which is inside the probe. */

void create_highlights()
	{
		WTp3 p;

		temp_object = WTuniverse_getobjects(); // first object in list
		while (temp_object != NULL) // create new boundary boxes
		{
			WTobject_getposition(temp_object, p);
			if ((temp_object != probe) &&
			    (p[X]<=(probeX()+50)) && (p[X]>=(probeX()-50)) &&
			    (p[Y]<=(probeY()+50)) && (p[Y]>=(probeY()-50)) && 
				(p[Z]<=(probeZ()+50)) && (p[Z]>=(probeZ()-50)))
			{
				temp_object2 = WTobject_boundingbox(temp_object, 3);
				objecttype = &dummyh;
				WTobject_setdata(temp_object2, (void*)objecttype);
			}
			temp_object = WTobject_next(temp_object);
		}

	}

/* This is the main procedure. It initializes the universe, creates the room
and the probe, initializes sensors, performs the start-up search, and starts
the universe. */

#if MSWIN
#include "std.h"	/* no other #includes after this one!! */
int WTSTD WTuser(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{	
	
	int i,i1,i2;
	_int16 fgcolor = 0x7fff;

	/* delete all temporary files */
	remove("c:\\inforoom\\textures\\temp\\*.*");

	for (i=0; i 485 )
			p[ii] = 485;
		if ( p[ii] < -485 )
			p[ii] = -485;
	}
	/* move viewpoint to constrained position */
	WTviewpoint_setposition(uview, p);
}

/* This function checks if the position of the probe is within the data
space defined by the axes. If it is outside, is is moved inside.

void constrain_probe(void)
{
	WTp3 p;
	int ii;

		
	WTobject_getposition(probe, p);

	for ( ii=0 ; ii<3 ; ii++ ) {
		if ( p[ii] > (factor*500)-50 )
			p[ii] = (factor*500)-50;
		if ( p[ii] < (-500*factor)+50 )
			p[ii] = (-500*factor)+50;
	}

	
	/* move object to constrained position */
	WTobject_setposition(probe, p);
}

/* Reads a string form the keyboard. Does not work with WTK, and is
currently not used. */

void read_string (char *buffer)
{
	while ((*buffer = getchar()) != '\n')
		buffer++;
	*buffer = '\0';
}

/* Prints a list in the text window of the variables available. */

void print_search_variables()
	{
	printf("\n 0: id\n");
	printf(" 1: author\n");
	printf(" 2: title\n");
	printf(" 3: subtitle\n");
	printf(" 4: published in\n");
	printf(" 5: publisher\n");
	printf(" 6: year\n");
	printf(" 7: size\n");
	printf(" 8: type\n");
	printf(" 9: base\n");
	printf("10: keywords\n");
	}

/* Prompts the user for an x variable and the boundary values of this. */

void input_x()
{	char string[50];

	printf("\nEnter X variable (0-10):\n");
	print_search_variables();
	gets(string);
	x_var = atoi (string);

	printf("Enter X minimum value:\n");
	gets(string);
	strcpy(x_min_text,string);

	printf("Enter X maximum value:\n");
	gets(string);
	strcpy(x_max_text,string);
}

/* Prompts the user for a y variable and the boundary values of this. */

void input_y()
{	char string[50];

	printf("\nEnter Y variable (0-10):\n");
	print_search_variables();
	gets(string);
	y_var = atoi (string);

	printf("Enter Y minimum value:\n");
	gets(string);
	strcpy(y_min_text,string);

	printf("Enter Y maximum value:\n");
	gets(string);
	strcpy(y_max_text,string);
}		

/* Prompts the user for a z variable and the boundary values of this. */

void input_z()
{	char string[50];

	printf("\nEnter Z variable (0-10):\n");
	print_search_variables();
	gets(string);
	z_var = atoi (string);

	printf("Enter Z minimum value:\n");
	gets(string);
	strcpy(z_min_text,string);

	printf("Enter Z maximum value:\n");
	gets(string);
	strcpy(z_max_text,string);
}

/* Computes new values for the x boundaries based on the position of the probe
and performs a new search. Is used to explode the probe. */

void compute_x()
{
	x_min_old = x_min;
	x_max_old = x_max;
	
	x_min = (500*factor+probeX()-50)*(x_max_old-x_min_old)/(1000*factor)+x_min_old;
	x_max = (500*factor+probeX()+50)*(x_max_old-x_min_old)/(1000*factor)+x_min_old;
		
	if ((x_var == year) || (x_var == base) || (x_var == size))
	{
		itoa(x_min, x_min_text, 10);
		itoa(x_max, x_max_text, 10);
	}
	else
	{
		convert_number2string(x_min, x_min_text);
		convert_number2string(x_max, x_max_text);
	}
		
	search(x_var, "search1", x_min_text, x_max_text);
}

/* Computes new values for the y boundaries based on the position of the probe
and performs a new search. Is used to explode the probe. */

void compute_y()
{
	y_min_old = y_min;
	y_max_old = y_max;

	y_min = (500*factor+probeY()-50)*(y_max_old-y_min_old)/(1000*factor)+y_min_old;
	y_max = (500*factor+probeY()+50)*(y_max_old-y_min_old)/(1000*factor)+y_min_old;
		
	if ((y_var == year) || (y_var == base) || (y_var == size))
	{
		itoa(y_min, y_min_text, 10);
		itoa(y_max, y_max_text, 10);
	}
	else
	{
		convert_number2string(y_min, y_min_text);
		convert_number2string(y_max, y_max_text);
	}
		
	search(y_var, "search2", y_min_text, y_max_text);
}

/* Computes new values for the z boundaries based on the position of the probe
and performs a new search. Is used to explode the probe. */

void compute_z()
{
	z_min_old = z_min;
	z_max_old = z_max;
		
	z_min = (500*factor-probeZ()-50)*(z_max_old-z_min_old)/(1000*factor)+z_min_old;
	z_max = (500*factor-probeZ()+50)*(z_max_old-z_min_old)/(1000*factor)+z_min_old;

	if ((z_var == year) || (z_var == base) || (z_var == size))
	{
		itoa(z_min, z_min_text, 10);
		itoa(z_max, z_max_text, 10);
	}
	else
	{
		convert_number2string(z_min, z_min_text);
		convert_number2string(z_max, z_max_text);
	}
		
	search(z_var, "search3", z_min_text, z_max_text);
}

/* The function actionfn is called before each frame is calculated. It processes
keyboard and sensor input from the user. The effect of each input is explained
within the function.
When all input is process, the function makes sure, that the users viewpoint
and the probe are within their boundaries. The probe's shadows are updated,
and so are the object higlights. */

static void actionfn()
{
	long ntotal;
	short key;
	long data, mousedata;					 
	WTmouse_rawdata *raw;
	WTp2 *pos2D;
	WTp3 p, p2, point_pos;
	WTq q, point_ori;
	WTobject *path_object;
	WTpoly *fors, *fors2;
	WTpath *rough_path, *fine_path;
	WTp3 pos_start, pos_end, ori_end_vector, dir, dir2;
	WTq ori_start, ori_end;
	WTpq location_start, location_end, location_point;
	WTpathnode *start_node, *end_node, *point_node, *end_node2, *start_node2;
	float temp;
	int choice,i;
	long indexno;
	float nodes;
	int database;
	char mediatype;
	int found_object;

	char string[50];
	char *result;
	
	char output_filename[100];
	char temp_streng[20];

	char id_string[12], author_string[75],title_string[500],subtitle_string[500];
	char published_in_string[50],publisher_string[75],year_string[15];
	char size_string[10],type_string[2],base_string[3],keyword1_string[100];
	char keyword2_string[100],keyword3_string[100],keyword4_string[100];
	char keyword5_string[100];

	char type_expanded_string[20];
	char base_expanded_string[20];

	
	/* get key presses, if any */
	key = WTkeyboard_getlastkey();

	// get mouse and joystick data
	mousedata = WTsensor_getmiscdata(sensor1);
	data = WTsensor_getmiscdata(sensor3);
	raw = (WTmouse_rawdata *) WTsensor_getrawdata(sensor1);
	pos2D = raw->pos;

	/* The 'd' key toggles distant titles on/off. */

	if (key == 'd')
		if (distant_title)
			{
			distant_title = FALSE;
			printf("\nDistant title switched off.\n");
			}
		else
			{
			distant_title = TRUE;
			printf("\nDistant title switched on.\n");
			}	
	
	/* The 'h' key activates the homing function. The current position
	and the home position are added to a path, and a number of points
	depending on distance from home position are calculated. Finally
	the path is played. */

	if (key=='h')
		{
		if (rhymes) printf("\nIf you're lost without a clue\nlet me change your point of view.\n");
		
		//create new path
		rough_path = WTpath_new(probe);

		//get start position
		WTviewpoint_getposition(uview,pos_start);
		WTviewpoint_getorientation(uview,ori_start);
	
		convert_xyz2pos(450,450,-450,pos_end);
		convert_xyzw2ori(-0.3696,-0.2840,0.8799,0.0926 ,ori_end); 

		WTp3_copy(pos_start,location_start.p);
		WTp3_copy(pos_end,location_end.p);
		WTq_copy(ori_start,location_start.q);
		WTq_copy(ori_end,location_end.q);

		//add start node
		start_node = WTpathnode_new(&location_start);
		end_node = WTpathnode_new(&location_end);
		WTpath_appendnode(rough_path, start_node);

		//acid trip only
		if (syretrip) 
		{
				convert_xyzw2ori(-0.0044, -0.0000, 0.0559, -0.0000, trip_ori);
				WTp3_copy(center,trip_location.p);
				WTq_copy(trip_ori, trip_location.q);
				trip_node = WTpathnode_new(&trip_location);
				WTpath_appendnode(rough_path, trip_node);
		}

		//add end node
		WTpath_appendnode(rough_path, end_node);
		
		//calculate number of intermediary points
		nodes = WTp3_distance(pos_start,pos_end)/25.0;
		if (nodes < 5)
			nodes = 5;

		fine_path = WTpath_interpolate(rough_path,nodes, WTPATH_BEZIER);
		WTpath_play(fine_path);

		}						   

	/* The 'g' key activates the guided tour. It computes a path from
	the users position to the home position, around the swarm, to the
	home position again, and back to the starting position. In the end
	the path is played. */

 	if (key=='g') //guided tour
		{
		if (rhymes)
		{
			printf("\nIf you're somewhat confused\nand the overview is poor");
			printf("\nI think you'll be amused\nby this lovely guided tour.\n");
		}
		
		rough_path = WTpath_new(probe);

		WTviewpoint_getposition(uview,pos_start);	 // starting point
		WTviewpoint_getorientation(uview,ori_start);
		WTp3_copy(pos_start,location_start.p);
		WTq_copy(ori_start,location_start.q);
		start_node = WTpathnode_new(&location_start);
		WTpath_appendnode(rough_path, start_node);

		convert_xyz2pos(410,410,-410,pos_end);		 // home position
		convert_xyzw2ori(-0.3696,-0.2840,0.8799,0.0926 ,ori_end); 
		WTp3_copy(pos_end,location_end.p);
		WTq_copy(ori_end,location_end.q);
		end_node = WTpathnode_new(&location_end);
		WTpath_appendnode(rough_path, end_node);
	
		convert_xyz2pos(0,0,-410,point_pos);		// point 1
		//convert_xyzw2ori(); 
		WTp3_copy(point_pos,location_point.p);
		WTq_copy(xy_ori,location_point.q);
		point_node = WTpathnode_new(&location_point);
		WTpath_appendnode(rough_path, point_node);

		convert_xyz2pos(-410,0,0,point_pos);		// point 2
		convert_xyzw2ori(0.7312, -0.0006, 0.6822, 0.0006, point_ori); 
		WTp3_copy(point_pos,location_point.p);
		WTq_copy(point_ori,location_point.q);
		point_node = WTpathnode_new(&location_point);
		WTpath_appendnode(rough_path, point_node);

		convert_xyz2pos(0,0,410,point_pos);		   // point 3
		convert_xyzw2ori(0.9985, -0.0000, 0.0547, 0.0008, point_ori); 
		WTp3_copy(point_pos,location_point.p);
		WTq_copy(point_ori,location_point.q);
		point_node = WTpathnode_new(&location_point);
		WTpath_appendnode(rough_path, point_node);

		convert_xyz2pos(410,0,0,point_pos);	   // point 4
		convert_xyzw2ori(-0.7068, -0.0006, 0.7074, -0.0006, point_ori); 
		WTp3_copy(point_pos,location_point.p);
		WTq_copy(point_ori,location_point.q);
		point_node = WTpathnode_new(&location_point);
		WTpath_appendnode(rough_path, point_node);

		end_node2 = WTpathnode_copy(end_node);
		WTpath_appendnode(rough_path, end_node2);	// home position

		start_node2 = WTpathnode_copy(start_node);
		WTpath_appendnode(rough_path, start_node2);	// starting position
		
		fine_path = WTpath_interpolate(rough_path,30, WTPATH_BEZIER);
		WTpath_play(fine_path);

		}						   

	/* The '1' key moves the viewpoint to a position in front of wall
	number 1 (XY).  The current position and the ending position are
	added to a path, and a number of points depending on distance from
	ending position are calculated. Finally the path is played. */

	if (key=='1')
		{
		if (rhymes)
			printf("\nIf you looked for number one\nthat's exactly where you've gone.\n");
		
		rough_path = WTpath_new(probe);
		WTviewpoint_getposition(uview,pos_start);
		WTviewpoint_getorientation(uview,ori_start);
	
		convert_xyz2pos(-50,0,100,pos_end);

		WTp3_copy(pos_start,location_start.p);
		WTp3_copy(pos_end,location_end.p);
		WTq_copy(ori_start,location_start.q);
		WTq_copy(xy_ori,location_end.q);
		start_node = WTpathnode_new(&location_start);
		end_node = WTpathnode_new(&location_end);
		WTpath_appendnode(rough_path, start_node);

		// acid trip only
		if (syretrip) 
		{
				convert_xyzw2ori(-0.0044, -0.0000, 0.0559, -0.0000, trip_ori);
				WTp3_copy(center,trip_location.p);
				WTq_copy(trip_ori, trip_location.q);
				trip_node = WTpathnode_new(&trip_location);
				WTpath_appendnode(rough_path, trip_node);
		}

		WTpath_appendnode(rough_path, end_node);
		
		nodes = WTp3_distance(pos_start,pos_end)/25.0;
		if (nodes < 5)
			nodes = 5;

		fine_path = WTpath_interpolate(rough_path,nodes, WTPATH_BEZIER);
		WTpath_play(fine_path);

		}						   

	/* The '2' key moves the viewpoint to a position in front of wall
	number 2 (XZ).  The current position and the ending position are
	added to a path, and a number of points depending on distance from
	ending position are calculated. Finally the path is played. */

 	if (key=='2')
		{
		if (rhymes)
			printf("\nNumber two can now be seen\nin the middle of your screen.\n");
		
		rough_path = WTpath_new(probe);
		WTviewpoint_getposition(uview,pos_start);
		WTviewpoint_getorientation(uview,ori_start);
	
		convert_xyz2pos(-50,-100,20,pos_end);

		WTp3_copy(pos_start,location_start.p);
		WTp3_copy(pos_end,location_end.p);
		WTq_copy(ori_start,location_start.q);
		WTq_copy(xz_ori,location_end.q);
		start_node = WTpathnode_new(&location_start);
		end_node = WTpathnode_new(&location_end);
		WTpath_appendnode(rough_path, start_node);

		//acid trip only
		if (syretrip) 
		{
				convert_xyzw2ori(-0.0044, -0.0000, 0.0559, -0.0000, trip_ori);
				WTp3_copy(center,trip_location.p);
				WTq_copy(trip_ori, trip_location.q);
				trip_node = WTpathnode_new(&trip_location);
				WTpath_appendnode(rough_path, trip_node);
		}
		WTpath_appendnode(rough_path, end_node);
		
		nodes = WTp3_distance(pos_start,pos_end)/25.0;
		if (nodes < 5)
			nodes = 5;

		fine_path = WTpath_interpolate(rough_path,nodes, WTPATH_BEZIER);
		WTpath_play(fine_path);

		}						   

	/* The '3' key moves the viewpoint to a position in front of wall
	number 3 (YZ).  The current position and the ending position are
	added to a path, and a number of points depending on distance from
	ending position are calculated. Finally the path is played. */

	if (key=='3')
		{
		if (rhymes)
			printf("\nDo not worry! Have no fear!\nNumber three shall soon be near!\n");
		
		rough_path = WTpath_new(probe);
		WTviewpoint_getposition(uview,pos_start);
		WTviewpoint_getorientation(uview,ori_start);
	
		convert_xyz2pos(-100,0,50,pos_end);

		WTp3_copy(pos_start,location_start.p);
		WTp3_copy(pos_end,location_end.p);
		WTq_copy(ori_start,location_start.q);
		WTq_copy(yz_ori,location_end.q);
		start_node = WTpathnode_new(&location_start);
		end_node = WTpathnode_new(&location_end);
		WTpath_appendnode(rough_path, start_node);

		// acid trip only
		if (syretrip) 
		{
				convert_xyzw2ori(-0.0044, -0.0000, 0.0559, -0.0000, trip_ori);
				WTp3_copy(center,trip_location.p);
				WTq_copy(trip_ori, trip_location.q);
				trip_node = WTpathnode_new(&trip_location);
				WTpath_appendnode(rough_path, trip_node);
		}

		WTpath_appendnode(rough_path, end_node);
		
		nodes = WTp3_distance(pos_start,pos_end)/25.0;
		if (nodes < 5)
			nodes = 5;

		fine_path = WTpath_interpolate(rough_path,nodes, WTPATH_BEZIER);
		WTpath_play(fine_path);

		}						   


	/* The 'q' key causes the universe and the program to stop. */

	if ( key=='q' ) 
		WTuniverse_stop();

	/* The '?' key activates a help function. Currently not very
	helpful... */

	if ( key=='?' ) 
		printf("\nPress 'q' key to quit\n");
			
	/* The 'i' key gives information about the viewpoint's position
	and orientation, the framerate and total number of polygons in
	the universe. Only useful for developers. */

	if ( key=='i' ) 
		{
			ntotal = WTuniverse_npolygons();
			WTviewpoint_getposition(uview, p);
			WTviewpoint_getorientation(uview, q);
			printf("\nviewpoint: \n");
			WTp3_print(p, "Position: ");
			WTq_print(q, "Orientation: ");
			printf("Framerate: \t%f\n",WTuniverse_framerate());
			printf("Antal polygoner: %d\n", ntotal);
		};

	/* The 'c' key prompts the user for new values and boundaries for
	the x, y and z variables. A new search is performed and the room is
	redrawn. */

	if (key=='c') // change everything
	{
		input_x();		
		input_y();
		input_z();		

		search (x_var, "search1", x_min_text, x_max_text);
		search (y_var, "search2", y_min_text, y_max_text);
		search (z_var, "search3", z_min_text, z_max_text);

		merge_searchfiles ("search1", "search2", "search3");

		redraw_room();
	}

	/* The 'x' key prompts the user for new values and boundaries for
	the x variable. A new search is performed and the room is
	redrawn. */

 	if (key=='x') // change x
	{
		input_x();		

		search (x_var, "search1", x_min_text, x_max_text);

		merge_searchfiles ("search1", "search2", "search3");

		redraw_room();
	

	}

	/* The 'y' key prompts the user for new values and boundaries for
	the y variable. A new search is performed and the room is
	redrawn. */

	if (key=='y') // change y
	{
		input_y();

		search (y_var, "search2", y_min_text, y_max_text);

		merge_searchfiles ("search1", "search2", "search3");

		redraw_room();
	}

	/* The 'z' key prompts the user for new values and boundaries for
	the z variable. A new search is performed and the room is
	redrawn. */

	if (key=='z') // change z
	{
		input_z();		

		search (z_var, "search3", z_min_text, z_max_text);

		merge_searchfiles ("search1", "search2", "search3");

		redraw_room();
	}

	/* The 't' key toggles title pages on/off. When titles are on,
	all object data are displayed on the front pages of all objects.
	Turning titles on, turns short titles off. */

	if (key=='t') //toggle titles on/off
	{
		if (titles_on)		
			{
			titles_on = FALSE;
			printf("\nTitles switched off.\n");
			}		
		else
			{
			titles_on = TRUE;
			short_titles_on = FALSE;
			printf("\nTitles switched on.\n");
			}

		if (!(point))
			redraw_room();
	}

	/* The 's' key toggles short titles on/off. When short titles
	are on, the author and title of the objects are displayed on
	all objects. Turning short titles on, turns title pages (long
	titles) off. */

	if (key=='s') //toggle short titles on/off
	{
		if (short_titles_on)		
			{
			short_titles_on = FALSE;
			printf("\nShort titles switched off.\n");
			}		
		else
			{
			short_titles_on = TRUE;
			titles_on = FALSE;
			printf("\nShort titles switched on.\n");
			}

		if (!(point))
			redraw_room();
	}

	/* The 'n' key changes the place, where object data are displayed
	when an object is selected by clicking on it with the middle mouse
	button. When text info is on, data are displayed in the text
	window; when text info is off, data are displayed on the front of
	the object (in the same way as a title page. */

	if (key=='n') //toggle book data in text window on/off
	{
		if (text_info)		
			{
			text_info = FALSE;
			printf("\nData in text window switched off.\n");
			}		
		else
			{
			text_info = TRUE;
			printf("\nData in text window switched on.\n");
			}
	}

	/* The 'l' key toggles the highlighting function on/off. When
	highlighting is on, a boundary box is created around each object
	within the probe. */

	if (key=='l') //toggle highlight on/off
	{
		if (highlight)		
			{
			highlight = FALSE;
			printf("\nHighlighting switched off.\n");
			delete_highlights();// remove bounding boxes
			}		
		else
			{
			highlight = TRUE;
			printf("\nHighlighting switched on.\n");
			}
	}


	/* The 'o' key toggles thin objects on/off. When thin objects are
	off, objects are boxes consisting of six polygons; when thin
	objects are on, objects are single polygons, thus increasing the
	performance of the system. When thin thin objects are turned on,
	title pages are automatically disabled. */

 	if (key=='o') //toggle thin objects on/off
	{
		if (thin_objects_on)		
			{
			thin_objects_on = FALSE;
			printf("\nThin objects switched off.\n");
			}		
		else
			{
			thin_objects_on = TRUE;
			titles_on = FALSE;
			printf("\nThin objects switched on.\n");
			}

		if (!(point))
			redraw_room();
	}


	/* The 'u' key toggles object textures on/off. When object
	textures are off, objects are displayed using colors, which
	increases performance. Turning textures off, also turns
	title pages off. */

 	if (key=='u') //toggle textures on/off
	{
		if (textures_on)		
			{
			textures_on = FALSE;
			titles_on = FALSE;
			printf("\nTextures switched off.\n");
			}		
		else
			{
			textures_on = TRUE;
			printf("\nTextures switched on.\n");
			}

		if (!(point))
			redraw_room();
	}


	/*The 'm' key changes the visualization mode. The user is prompted
	to enter w, s, p, c or a for wallmode, swarmmode, pointmode,
	colormode and automode, respectively. After achange of mode, the
	room is redrawn. */

	if (key=='m') //change mode
	{
		if (rhymes)
		{
			printf("\nChoose a mode\nbut choose with care");
			printf("\nThe longest road\nyou shall not fare.\n");
		}
		
		printf("\nChange mode (W=wall, S=swarm, P=point, C=Color, A=auto):\n");
		gets(string);
		if (string[0]=='w')
			{
			point = FALSE;
			swarm = FALSE;
		    wall = TRUE;
			automode = FALSE;
			color_mode = FALSE;
			redraw_room();	
			}
		if (string[0]=='s')
			{
			point = FALSE;
			swarm = TRUE;
		    wall = FALSE;
			automode = FALSE;
			color_mode = FALSE;
			redraw_room();	
			}
		if (string[0]=='p')
			{
			point = TRUE;
			swarm = FALSE;
		    wall = FALSE;
			automode = FALSE;
			color_mode = FALSE;
			redraw_room();	
			}
		if (string[0]=='a')
			{
			point = FALSE;
			swarm = FALSE;
		    wall = FALSE;
			automode = TRUE;
			color_mode = FALSE;
			redraw_room();	
			}
		if (string[0]=='c')
			{
			point = FALSE;
			swarm = FALSE;
		    wall = FALSE;
			automode = FALSE;
			color_mode = TRUE;
			redraw_room();	
			}
		
	}

	/* The 'arrow up' key moves the probe 10 units towards the XY
	wall. */

	if (key == WTKEY_UPARROW)
	{
		object_setposXYZ (probe, probeX(), probeY(), probeZ()+10);
	}

	/* The 'arrow down' key moves the probe 10 units away from the XY
	wall. */

	if (key == WTKEY_DOWNARROW)
	{
		object_setposXYZ (probe, probeX(), probeY(), probeZ()-10);
	}

	/* The 'arrow left' key moves the probe 10 units away from the YZ
	wall. */

	if (key == WTKEY_LEFTARROW)
	{
		object_setposXYZ (probe, probeX()+10, probeY(), probeZ());
	}

	/* The 'arrow right' key moves the probe 10 units towards the YZ
	wall. */

	if (key == WTKEY_RIGHTARROW)
	{
		object_setposXYZ (probe, probeX()-10, probeY(), probeZ());
	}

	/* The '-' key moves the probe 10 units away from the XZ wall. */

	if (key == '-')
	{
		object_setposXYZ (probe, probeX(), probeY()+10, probeZ());
	}

	/* The '+' key moves the probe 10 units towards the XZ wall. */

	if (key == '+')
	{
		object_setposXYZ (probe, probeX(), probeY()-10, probeZ());
	}

	/* The 'e' key (or the left button of the 3D mouse) activates the
	explode function. If activated in pointmode, wallmode or colormode,
	the user is prompted for the wall to explode; when the selction is
	done, the boundaries of the two dimensions visualized by that wall
	are changed to those of the probe.
	If activated in swarm mode, the boundaries of all three variables
	are changed to those of the probe.
	After the boundaries are changed, a new search is performed, and
	the room is redrawn. */

	if ((key == 'e') || (data & WTLOGITECH_LEFTBUTTON))// explode probe
	{
		if ((point == TRUE) || (wall == TRUE) || (color_mode == TRUE))
		{
			printf("\nChoose wall to explode (XY = 1, XZ = 2, YZ = 3):\n");
			gets(string);
			choice = atoi(string);
			switch (choice)
			{
				case 1: 	compute_x();
					compute_y();
					break;
				case 2:	compute_x();
					compute_z();
					break;
				case 3:	compute_y();
					compute_z();
					break;
			}
		}
		else
		{
			compute_x();
			compute_y();
			compute_z();
		}

		if (rhymes) printf ("\nPlease be patient while I load\nwhat you asked me to explode.\n");
		merge_searchfiles ("search1", "search2", "search3");

		redraw_room();

	}
	
	/* The 'f' key prompts the user to enter a compression factor.
	The compression factor determines the part of the room and walls
	that is used for displaying data. The default value is 50% (that
	is, 50% on each dimension). */

	if (key == 'f') // change compression Factor
	{
		if (rhymes)
			printf("\nCompression gives a different view\nA number I must ask of you:\n");
		else
			printf("\nEnter compression factor (xx percent):\n");
		gets(string);
		factor = atoi(string)/100.0;
		redraw_room();
	}
	
	/* The 'r' key resets the room. All variables and boundaries are
	resat to default values, a new search is performed and the room
	is redrawn. */

	if (key == 'r')
	{
		if (rhymes)
		{
			printf("\nIf you stand\nin no man's land\nand everything is strange");
			printf("\nDo not despair\nbut be aware\nhow quickly things can change.\n");
		}
		else
			printf ("\nResetting room.\n");
		
		x_var = year;
		y_var = publisher;
		z_var = author;

		strcpy(x_min_text ,"1990");
		strcpy(x_max_text ,"1995");
		strcpy(y_min_text ,"A");
		strcpy(y_max_text ,"Z");
		strcpy(z_min_text ,"A");
		strcpy(z_max_text ,"Z");

		search (x_var, "search1", x_min_text, x_max_text);
		search (y_var, "search2", y_min_text, y_max_text);
		search (z_var, "search3", z_min_text, z_max_text);

		merge_searchfiles ("search1", "search2", "search3");

		redraw_room();
	}

	/* The 'p' key toggles the visibility of the probe and its shadows.
	When the probe is invisible, it does not block selection of
	objects. */

	if (key == 'p')
	{
		if (WTobject_getvisibility(probe))
		{
			printf("\nProbe is now invisible.\n");
			WTobject_setvisibility(probe, FALSE);
			WTobject_setvisibility(shadow_xy1, FALSE);
			WTobject_setvisibility(shadow_xz2, FALSE);
			WTobject_setvisibility(shadow_yz2, FALSE);
		}
		else
		{
			printf("\nProbe is now visible.\n");
			WTobject_setvisibility(shadow_xy1, TRUE);
			WTobject_setvisibility(shadow_xz2, TRUE);
			WTobject_setvisibility(shadow_yz2, TRUE);
			WTobject_setvisibility(probe, TRUE);
		}
	}

	/* The 'b' key activates the rhyme function, resulting in a little
	rhyme being displayed upon activating certain functions.
	Very amusing. */

	if (key == 'b')
	{
		if (rhymes)
		{
			printf ("\nWords without rhyme\nare a waste of time\n");
			rhymes = FALSE;
		}
		else
		{
			printf ("\nExcellent choice:\nRhymes from the boyz!\n");
			rhymes = TRUE;
		}
	}

	/* The 'a' key activates the acid trip function. When selected, all
	flying not done using free-flight becomes a rather strange
	experience. The room is turned inside out and stretched in peculiar
	ways. This is accomplished by inserting an extra node in the paths;
	this node's position is the center of the room, and it's orientation
	is an invalid quaternion. Not useful at all, unless you want to get
	dizzy. */

	if (key == 'a')
	{
		if (syretrip)
		{
			printf("\nBoring...");
			syretrip = FALSE;
		}
		else
		{
			printf("\nYahoo!! Syretrip!");
			syretrip = TRUE;
		}
	}

	/* Clicking with the middle button of the mouse on an object
	selects that object. What happens the depends on position of the
	users viewpoint and the values of text_info and distant_titles.

	If distant_titles are off, and the user is not right in front of
	object, a path is calculated and the user is flown to the object.

	If the user is already in front of the object, or distant_titles
	are on, the objects data are displayed. If text_info is on, they
	are displayed in the text window, otherwise they are displayed on
	the object. */

	if (mousedata & WTMOUSE_MIDDLEBUTTON) 
		{
			temp_object = WTuniverse_pickobject(pos2D);
			objecttype = (long*)WTobject_getdata(temp_object);
			if (*objecttype >= 0) //it's a data object
			{
				//calculate path
				rough_path = WTpath_new(probe);
				WTviewpoint_getposition(uview,pos_start);
				WTviewpoint_getorientation(uview,ori_start);
				WTobject_getposition(temp_object, pos_end);
				
				WTp3_copy (pos_end, p);
				
				if (pos_end[X] <= -480.0) 
					{
					pos_end[X] += factor*40.0;
					WTq_copy(yz_ori, ori_end);
					}
				else
					if (pos_end[Y] <= -480.0) 
						{
						pos_end[Y] += factor*40.0;
						WTq_copy(xz_ori, ori_end);
						}
					else
						{
						pos_end[Z] -= factor*40.0;
						WTq_copy(xy_ori, ori_end);
						}
				//already there, or distant_titles on: show data on object or in window
				if (WTp3_equal(pos_start, pos_end) || distant_title)
				{
					if (text_info) //data in window
 					{
						indexno = *objecttype;
						database = _open( "library", _O_RDONLY);
						_lseek (database, indexno*RECORD_SIZE, SEEK_SET);
						_read (database, id_string, var_len[id]);
						_read (database, author_string, var_len[author]);
						_read (database, title_string, var_len[title]);
						_read (database, subtitle_string, var_len[subtitle]);
						_read (database, published_in_string, var_len[published_in]);
						_read (database, publisher_string, var_len[publisher]);
						_read (database, year_string, var_len[year]);
						_read (database, size_string, var_len[size]);
						_read (database, type_string, var_len[type]);
						_read (database, base_string, var_len[base]);
						_read (database, keyword1_string, var_len[keywords]);
						_read (database, keyword2_string, var_len[keywords]);
						_read (database, keyword3_string, var_len[keywords]);
						_read (database, keyword4_string, var_len[keywords]);
						_read (database, keyword5_string, var_len[keywords]);
						_close(database);

						if (type_string[0] != 0)
							strcpy(type_expanded_string, type_texts[type_string[0]-97]);
						else
							strcpy(type_expanded_string, "");
						if (base_string[0] != 0)
							strcpy(base_expanded_string, base_texts[atoi(base_string)]);
						else
							strcpy(base_string, "");
					
						printf("\nBook data: \n");
						printf("Id..........: %s\n",id_string);
						printf("Author......: %s\n",author_string);
						printf("Title.......: %s\n",title_string);
						printf("Subtitle....: %s\n",subtitle_string);
						printf("Published in: %s\n",published_in_string);
						printf("Publisher...: %s\n",publisher_string);
						printf("Year........: %s\n",year_string);
						printf("Size........: %s\n",size_string);
						printf("Type........: %s\n",type_expanded_string);
						printf("Base........: %s\n",base_expanded_string);
						printf("Keywords....: %s\n",keyword1_string);
						if (keyword2_string[0]!=0)
							printf("              %s\n",keyword2_string);
						if (keyword3_string[0]!=0)
							printf("              %s\n",keyword3_string);
						if (keyword4_string[0]!=0)
							printf("              %s\n",keyword4_string);
						if (keyword5_string[0]!=0)
							printf("              %s\n",keyword5_string);
					}

					
					else //data on object
					{
						ltoa(*objecttype,temp_streng,10);
						strcpy(output_filename, "c:\\inforoom\\textures\\titles\\long\\");
						strcat(output_filename, temp_streng);
						strcat(output_filename, ".TGA");
					
						if (rhymes)
							printf("\nSit right back and wait for me\nThe title page you chose to see.\n");
						else
							printf("Reading titlepage from disk, please wait...\n");
	
						database = _open( "library", _O_RDONLY);
						_lseek (database, (*objecttype)*RECORD_SIZE+var_pos[type], SEEK_SET);
						_read (database, type_string, var_len[type]);
						_lseek (database, (*objecttype)*RECORD_SIZE+var_pos[base], SEEK_SET);
						_read (database, base_string, var_len[base]);
						_close(database);

						mediatype = 'b';

						if (type_string[0] == 'c')
							mediatype = 'c';
						else
							if (atoi(base_string) == 5)
								mediatype = 'a';

						if (WTpoly_getid(WTobject_getpolys(temp_object)) == 1) //it's a title
						{
							indexno = *objecttype;
							WTobject_delete (temp_object);
							temp_object = WTuniverse_getobjects();
							found_object = FALSE;
							do
							{
								objecttype = (long*)WTobject_getdata(temp_object);
								WTobject_getposition(temp_object, p2);
								if ((*objecttype == indexno) && (WTp3_distance(p, p2) < 20.0)) 
									found_object = TRUE;
								else
									temp_object = WTobject_next(temp_object);
							}
							while (!found_object);
						}
						else
	 					{
							indexno = *objecttype;
							temp2_object = WTuniverse_getobjects();
							found_object = FALSE;
							do
							{
								objecttype = (long*)WTobject_getdata(temp2_object);
								WTobject_getposition(temp2_object, p2);
								if ((*objecttype == indexno) && (WTp3_distance(p, p2) < 20.0) &&
								(temp2_object != temp_object)) 
									found_object = TRUE;
								else
									temp2_object = WTobject_next(temp2_object);
							}
							while ((!found_object) && (temp2_object!=0));
							if (found_object)
								WTobject_delete (temp2_object);
							
						}




						switch (mediatype)
						{
							case 'a':	if (thin_objects_on)
										{
											fors = WTobject_getpolys(temp_object);
											if (!(WTpoly_settexture(fors, output_filename, FALSE, FALSE)))	
												{
												create_title_texture(indexno, "c:\\inforoom\\textures\\artikel3.tga",
												output_filename, 0x421, 0x7c00, 0, 'a');
												WTpoly_settexture(fors, output_filename, FALSE, FALSE);
												}

											WTpoly_rotatetexture(fors, 3.14159);
											//WTpoly_mirrortexture(fors);
										}
										else
										{
											fors = WTobject_getpolys(temp_object);
											for (i=0; i<3; i++)
												fors = WTpoly_next(fors);
											fors2 = WTpoly_next(WTpoly_next(fors));
											if (!(WTpoly_settexture(fors, output_filename, FALSE, FALSE)))	
												{
												create_title_texture(indexno, "c:\\inforoom\\textures\\artikel3.tga",
												output_filename, 0x421, 0x7c00, 0, 'a');
												WTpoly_settexture(fors, output_filename, FALSE, FALSE);
												WTpoly_settexture(fors2, output_filename, FALSE, FALSE);
												}
											else
												WTpoly_settexture(fors2, output_filename, FALSE, FALSE);

											WTpoly_rotatetexture(fors, 3.14159);
											WTpoly_rotatetexture(fors2, 3.14159);
										}

										break;

							case 'b':	if (thin_objects_on)
											{
											fors = WTobject_getpolys(temp_object);
											if (!(WTpoly_settexture(fors, output_filename, FALSE, FALSE)))	
												{
												create_title_texture(indexno, "c:\\inforoom\\textures\\bookfron.tga",
												output_filename, 0x421, 0x7c00, 0x7fff,'b');//0x7fe0, 0x7fff, 0
												WTpoly_settexture(fors, output_filename, FALSE, FALSE);
												}
				
											WTpoly_rotatetexture(fors, 3.14159);
											WTpoly_mirrortexture(fors);
											}
										else
											{
											fors = WTobject_getpolys(temp_object);
											for (i=0; i<3; i++)
												fors = WTpoly_next(fors);
											if (!(WTpoly_settexture(fors, output_filename, FALSE, FALSE)))	
												{
												create_title_texture(indexno, "c:\\inforoom\\textures\\bookfron.tga",
												output_filename, 0x421, 0x7c00, 0x7fff,'b');//0x7fe0, 0x7fff, 0
												WTpoly_settexture(fors, output_filename, FALSE, FALSE);
												}
				
											WTpoly_rotatetexture(fors, 3.14159);
											}
										break;
				
						}
					}
				}
				else //fly to object
				{					
					WTq_copy(ori_end,location_end.q);
					WTp3_copy(pos_start,location_start.p);
					WTq_copy(ori_start,location_start.q);
					WTp3_copy(pos_end,location_end.p);
		

					start_node = WTpathnode_new(&location_start);
					end_node = WTpathnode_new(&location_end);
					WTpath_appendnode(rough_path, start_node);

					//acid trip only
					if (syretrip) 
					{
							convert_xyzw2ori(-0.0044, -0.0000, 0.0559, -0.0000, trip_ori);
							WTp3_copy(center,trip_location.p);
							WTq_copy(trip_ori, trip_location.q);
							trip_node = WTpathnode_new(&trip_location);
							WTpath_appendnode(rough_path, trip_node);
					}

					WTpath_appendnode(rough_path, end_node);

					nodes = WTp3_distance(pos_start,pos_end)/25.0;
					if (nodes < 5)
						nodes = 5;

					fine_path = WTpath_interpolate(rough_path,nodes, WTPATH_BEZIER);

					WTpath_play(fine_path);
				}
			}
		}



	constrain_viewpoint();
	constrain_probe();

	//update highlights
	if (highlight && swarm)
	{
		delete_highlights();
		create_highlights();
	}

	//update shadows
	object_setposXYZ(shadow_xy1, probeX(), probeY(), 499);
	//object_setposXYZ(shadow_xy2, probeX(), probeY(), -499);
	//object_setposXYZ(shadow_xz1, probeX(), 499, probeZ());
	object_setposXYZ(shadow_xz2, probeX(), -499, probeZ());
	//object_setposXYZ(shadow_yz1, 499, probeY(), probeZ());
	object_setposXYZ(shadow_yz2, -499, probeY(), probeZ());

}