/************************************************************************** * hud.c -- hud defines and prototypes * * Written by Michele America, started September 1997. * * Copyright (C) 1997 Michele F. America - nomimarketing@mail.telepac.pt * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ * (Log is kept at end of this file) **************************************************************************/ #include #include #ifndef WIN32 # include /* for MAXINT */ #endif /* not WIN32 */ #include "hud.h" #include "../constants.h" #include "../Aircraft/aircraft.h" #include "../Scenery/mesh.h" #include "../Scenery/scenery.h" #include "../Math/mat3.h" #include "../Math/polar.h" #include "../Time/fg_timer.h" #include "../Math/fg_random.h" #include "../Weather/weather.h" // #define DEBUG #define drawOneLine(x1,y1,x2,y2) glBegin(GL_LINES); \ glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd(); /* textString - Bitmap font string */ static void textString(int x, int y, char *msg, void *font) { glRasterPos2f(x, y); while (*msg) { glutBitmapCharacter(font, *msg); msg++; } } /* strokeString - Stroke font string */ static void strokeString(int x, int y, char *msg, void *font) { glPushMatrix(); glTranslatef(x, y, 0); glScalef(.04, .04, .04); while (*msg) { glutStrokeCharacter(font, *msg); msg++; } glPopMatrix(); } /* Draws a measuring scale anywhere on the HUD Needs: HUD_scale struct */ static void drawscale( int type, int sub_type, int min_value, int orientation, int scr_pos, \ int scr_min, int scr_max, double total_amount, int min_div, int max_div, \ double cur_value ) { double vmin, vmax; int marker_x, marker_y; int mid_scr; // int scale_min, scale_max; register i; double factor; char TextScale[80]; int condition; vmin = cur_value-total_amount/2; vmax = cur_value+total_amount/2; mid_scr = scr_min+(scr_max-scr_min)/2; if( type == VERTICAL ) // Vertical scale { if( orientation == LEFT ) marker_x = scr_pos-6; else if( orientation == RIGHT ) marker_x = scr_pos; drawOneLine( scr_pos, scr_min, scr_pos, scr_max ); if( orientation == LEFT ) { drawOneLine( scr_pos-3, scr_min, scr_pos, scr_min ); drawOneLine( scr_pos-3, scr_max, scr_pos, scr_max ); drawOneLine( scr_pos, mid_scr, scr_pos+6, mid_scr ); } else if( orientation == RIGHT ) { drawOneLine( scr_pos, scr_min, scr_pos+3, scr_min ); drawOneLine( scr_pos, scr_max, scr_pos+3, scr_max ); drawOneLine( scr_pos, mid_scr, scr_pos-6, mid_scr ); } factor = (scr_max-scr_min)/total_amount; for( i=vmin; i<=vmax; i+=1 ) { if( sub_type == LIMIT ) condition = i>= min_value; else if( sub_type == NOLIMIT ) condition = 1; if( condition ) { marker_y = scr_min+(i-vmin)*factor; if( i%min_div==0 ) if( orientation == LEFT ) { drawOneLine( marker_x+3, marker_y, marker_x+6, marker_y ); } else if( orientation == RIGHT ) { drawOneLine( marker_x, marker_y, marker_x+3, marker_y ); } if( i%max_div==0 ) { drawOneLine( marker_x, marker_y, marker_x+6, marker_y ); sprintf( TextScale, "%d", i ); if( orientation == LEFT ) { textString( marker_x-8*strlen(TextScale)-2, marker_y-4, TextScale, GLUT_BITMAP_8_BY_13 ); } else if( orientation == RIGHT ) { textString( marker_x+10, marker_y-4, TextScale, GLUT_BITMAP_8_BY_13 ); } } } } } if( type == HORIZONTAL ) // Horizontal scale { if( orientation == TOP ) marker_y = scr_pos; else if( orientation == BOTTOM ) marker_y = scr_pos-6; drawOneLine( scr_min, scr_pos, scr_max, scr_pos ); if( orientation == TOP ) { drawOneLine( scr_min, scr_pos, scr_min, scr_pos-3 ); drawOneLine( scr_max, scr_pos, scr_max, scr_pos-3 ); drawOneLine( mid_scr, scr_pos, mid_scr, scr_pos-6 ); } else if( orientation == BOTTOM ) { drawOneLine( scr_min, scr_pos, scr_min, scr_pos+3 ); drawOneLine( scr_max, scr_pos, scr_max, scr_pos+3 ); drawOneLine( mid_scr, scr_pos, mid_scr, scr_pos+6 ); } factor = (scr_max-scr_min)/total_amount; for( i=vmin; i<=vmax; i+=1 ) { if( sub_type == LIMIT ) condition = i>= min_value; else if( sub_type == NOLIMIT ) condition = 1; if( condition ) { marker_x = scr_min+(i-vmin)*factor; if( i%min_div==0 ) if( orientation == TOP ) { drawOneLine( marker_x, marker_y, marker_x, marker_y+3 ); } else if( orientation == BOTTOM ) { drawOneLine( marker_x, marker_y+3, marker_x, marker_y+6 ); } if( i%max_div==0 ) { sprintf( TextScale, "%d", i ); if( orientation == TOP ) { drawOneLine( marker_x, marker_y, marker_x, marker_y+6 ); textString( marker_x-4*strlen(TextScale), marker_y+14, TextScale, GLUT_BITMAP_8_BY_13 ); } else if( orientation == BOTTOM ) { drawOneLine( marker_x, marker_y, marker_x, marker_y+6 ); textString( marker_x+10, marker_y-4, TextScale, GLUT_BITMAP_8_BY_13 ); } } } } } } /* Draws a climb ladder in the center of the HUD Needs: HUD_ladder struct */ static void drawladder( struct HUD_ladder ladder ) { double vmin, vmax; double roll_value, pitch_value; double cos_roll, sin_roll; int marker_x, marker_y; int mid_scr; int scr_min, scr_max; int x_ini, x_end; int y_ini, y_end; int new_x_ini, new_x_end; int new_y_ini, new_y_end; register i; double factor; char TextLadder[80]; int condition; roll_value = (*ladder.load_roll)(); pitch_value = (*ladder.load_pitch)()*RAD_TO_DEG; vmin = pitch_value-ladder.width_units/2; vmax = pitch_value+ladder.width_units/2; scr_min = ladder.y_pos-(ladder.scr_height/2); scr_max = scr_min+ladder.scr_height; mid_scr = scr_min+(scr_max-scr_min)/2; marker_x = ladder.x_pos-ladder.scr_width/2; factor = (scr_max-scr_min)/ladder.width_units; for( i=vmin; i<=vmax; i+=1 ) { condition = 1; if( condition ) { marker_y = scr_min+(i-vmin)*factor; if( i%ladder.div_units==0 ) { sprintf( TextLadder, "%d", i ); if( ladder.scr_hole == 0 ) { if( i != 0 ) x_ini = ladder.x_pos-ladder.scr_width/2; else x_ini = ladder.x_pos-ladder.scr_width/2-10; y_ini = marker_y; x_end = ladder.x_pos+ladder.scr_width/2; y_end = marker_y; new_x_ini = ladder.x_pos+(x_ini-ladder.x_pos)*cos(roll_value)-\ (y_ini-ladder.y_pos)*sin(roll_value); new_y_ini = ladder.y_pos+(x_ini-ladder.x_pos)*sin(roll_value)+\ (y_ini-ladder.y_pos)*cos(roll_value); new_x_end = ladder.x_pos+(x_end-ladder.x_pos)*cos(roll_value)-\ (y_end-ladder.y_pos)*sin(roll_value); new_y_end = ladder.y_pos+(x_end-ladder.x_pos)*sin(roll_value)+\ (y_end-ladder.y_pos)*cos(roll_value); if( i >= 0 ) { drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); } else { glEnable(GL_LINE_STIPPLE); glLineStipple( 1, 0x00FF ); drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); glDisable(GL_LINE_STIPPLE); } textString( new_x_ini-8*strlen(TextLadder)-8, new_y_ini-4, TextLadder, GLUT_BITMAP_8_BY_13 ); textString( new_x_end+10, new_y_end-4, TextLadder, GLUT_BITMAP_8_BY_13 ); } else { if( i != 0 ) x_ini = ladder.x_pos-ladder.scr_width/2; else x_ini = ladder.x_pos-ladder.scr_width/2-10; y_ini = marker_y; x_end = ladder.x_pos-ladder.scr_width/2+ladder.scr_hole/2; y_end = marker_y; new_x_ini = ladder.x_pos+(x_ini-ladder.x_pos)*cos(roll_value)-\ (y_ini-ladder.y_pos)*sin(roll_value); new_y_ini = ladder.y_pos+(x_ini-ladder.x_pos)*sin(roll_value)+\ (y_ini-ladder.y_pos)*cos(roll_value); new_x_end = ladder.x_pos+(x_end-ladder.x_pos)*cos(roll_value)-\ (y_end-ladder.y_pos)*sin(roll_value); new_y_end = ladder.y_pos+(x_end-ladder.x_pos)*sin(roll_value)+\ (y_end-ladder.y_pos)*cos(roll_value); if( i >= 0 ) { drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); } else { glEnable(GL_LINE_STIPPLE); glLineStipple( 1, 0x00FF ); drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); glDisable(GL_LINE_STIPPLE); } textString( new_x_ini-8*strlen(TextLadder)-8, new_y_ini-4, TextLadder, GLUT_BITMAP_8_BY_13 ); x_ini = ladder.x_pos+ladder.scr_width/2-ladder.scr_hole/2; y_ini = marker_y; if( i != 0 ) x_end = ladder.x_pos+ladder.scr_width/2; else x_end = ladder.x_pos+ladder.scr_width/2+10; y_end = marker_y; new_x_ini = ladder.x_pos+(x_ini-ladder.x_pos)*cos(roll_value)-\ (y_ini-ladder.y_pos)*sin(roll_value); new_y_ini = ladder.y_pos+(x_ini-ladder.x_pos)*sin(roll_value)+\ (y_ini-ladder.y_pos)*cos(roll_value); new_x_end = ladder.x_pos+(x_end-ladder.x_pos)*cos(roll_value)-\ (y_end-ladder.y_pos)*sin(roll_value); new_y_end = ladder.y_pos+(x_end-ladder.x_pos)*sin(roll_value)+\ (y_end-ladder.y_pos)*cos(roll_value); if( i >= 0 ) { drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); } else { glEnable(GL_LINE_STIPPLE); glLineStipple( 1, 0x00FF ); drawOneLine( new_x_ini, new_y_ini, new_x_end, new_y_end ); glDisable(GL_LINE_STIPPLE); } textString( new_x_end+10, new_y_end-4, TextLadder, GLUT_BITMAP_8_BY_13 ); } } /* if( i%max_div==0 ) { drawOneLine( marker_x, marker_y, marker_x+6, marker_y ); sprintf( TextScale, "%d", i ); if( orientation == LEFT ) { textString( marker_x-8*strlen(TextScale)-2, marker_y-4, TextScale, GLUT_BITMAP_8_BY_13 ); } else if( orientation == RIGHT ) { textString( marker_x+10, marker_y-4, TextScale, GLUT_BITMAP_8_BY_13 ); } } */ } } } /* Draws an artificial horizon line in the center of the HUD (with or without a center hole) Needs: x_center, y_center, length, hole */ static void drawhorizon( struct HUD_horizon horizon ) { int x_inc1, y_inc1; int x_inc2, y_inc2; struct FLIGHT *f; double sin_bank, cos_bank; double bank_angle; // f = ¤t_aircraft.flight; bank_angle = (*horizon.load_value)(); // sin_bank = sin( FG_2PI-FG_Phi ); // cos_bank = cos( FG_2PI-FG_Phi ); sin_bank = sin(FG_2PI-bank_angle); cos_bank = cos(FG_2PI-bank_angle); x_inc1 = (int)(horizon.scr_width*cos_bank); y_inc1 = (int)(horizon.scr_width*sin_bank); x_inc2 = (int)(horizon.scr_hole*cos_bank); y_inc2 = (int)(horizon.scr_hole*sin_bank); if( horizon.scr_hole == 0 ) { drawOneLine( horizon.x_pos-x_inc1, horizon.y_pos-y_inc1, \ horizon.x_pos+x_inc1, horizon.y_pos+y_inc1 ); } else { drawOneLine( horizon.x_pos-x_inc1, horizon.y_pos-y_inc1, \ horizon.x_pos-x_inc2, horizon.y_pos-y_inc2 ); drawOneLine( horizon.x_pos+x_inc2, horizon.y_pos+y_inc2, \ horizon.x_pos+x_inc1, horizon.y_pos+y_inc1 ); } } /* Draws a label anywhere in the HUD Needs: HUD_label struct */ static void drawlabel( struct HUD_label label ) { char buffer[80]; char string[80]; int posincr; int lenstr; if( label.pre_str != NULL && label.post_str != NULL ) sprintf( buffer, "%s%s%s", label.pre_str, label.format, label.post_str ); else if( label.pre_str == NULL && label.post_str != NULL ) sprintf( buffer, "%s%s", label.format, label.post_str ); else if( label.pre_str != NULL && label.post_str == NULL ) sprintf( buffer, "%s%s", label.pre_str, label.format ); sprintf( string, buffer, (*label.load_value)() ); #ifdef DEBUG printf( buffer ); printf( "\n" ); printf( string ); printf( "\n" ); #endif lenstr = strlen( string ); if( label.justify == LEFT_JUST ) posincr = -lenstr*8; else if( label.justify == CENTER_JUST ) posincr = -lenstr*4; else if( label.justify == RIGHT_JUST ) posincr = 0; if( label.size == SMALL ) textString( label.x_pos+posincr, label.y_pos, string, GLUT_BITMAP_8_BY_13); else if( label.size == LARGE ) textString( label.x_pos+posincr, label.y_pos, string, GLUT_BITMAP_9_BY_15); } double get_speed() { struct FLIGHT *f; f = ¤t_aircraft.flight; return( FG_V_equiv_kts ); } double get_aoa() { struct FLIGHT *f; f = ¤t_aircraft.flight; return( FG_Gamma_vert_rad*RAD_TO_DEG ); } double get_roll() { struct FLIGHT *f; f = ¤t_aircraft.flight; return( FG_Phi ); } double get_pitch() { struct FLIGHT *f; f = ¤t_aircraft.flight; return( FG_Theta ); } double get_heading() { struct FLIGHT *f; f = ¤t_aircraft.flight; return( FG_Psi*RAD_TO_DEG ); } double get_altitude() { struct FLIGHT *f; double rough_elev; f = ¤t_aircraft.flight; rough_elev = mesh_altitude(FG_Longitude * RAD_TO_ARCSEC, FG_Latitude * RAD_TO_ARCSEC); return( FG_Altitude*FEET_TO_METER-rough_elev ); } void add_instrument( Hptr hud, HIptr instrument ) { HIptr instruments; instruments = hud->instruments; // while( ++instruments } Hptr fgHUDInit( struct AIRCRAFT current_aircraft, int color ) { Hptr hud; hud = (Hptr)calloc(sizeof(struct HUD),1); if( hud == NULL ) return( NULL ); hud->code = 123; hud->status = 0; // For now lets just hardcode a hud here . // In the future, hud information has to come from the same place // aircraft information came fgHUDAddHorizon( hud, 590, 50, 40, 20, get_roll ); fgHUDAddScale( hud, VERTICAL, 220, 100, 280, 5, 10, LEFT, LEFT, 0, 100, get_speed ); fgHUDAddScale( hud, VERTICAL, 440, 100, 280, 1, 5, RIGHT, RIGHT, -400, 25, get_aoa ); fgHUDAddScale( hud, HORIZONTAL, 280, 220, 440, 5, 10, TOP, TOP, 0, 50, get_heading ); fgHUDAddLabel( hud, 180, 85, SMALL, NOBLINK, RIGHT_JUST, NULL, " Kts", "%5.0f", get_speed ); fgHUDAddLabel( hud, 180, 73, SMALL, NOBLINK, RIGHT_JUST, NULL, " m", "%5.0f", get_altitude ); fgHUDAddLadder( hud, 330, 190, 90, 180, 70, 10, NONE, 45, get_roll, get_pitch ); return( hud ); } Hptr fgHUDAddHorizon( Hptr hud, int x_pos, int y_pos, int length, \ int hole_len, double (*load_value)() ) { struct HUD_horizon *horizon; struct HUD_instr *instrument; HIptr tmp_first, tmp_next; tmp_first = hud->instruments; if( tmp_first != NULL ) tmp_next = tmp_first->next; else tmp_next = NULL; instrument = (HIptr)calloc(sizeof(struct HUD_instr),1); if( instrument == NULL ) return( NULL ); horizon = (struct HUD_horizon *)calloc(sizeof(struct HUD_horizon),1); if( horizon == NULL ) return( NULL ); instrument->type = ARTIFICIAL_HORIZON; instrument->instr = *horizon; instrument->instr.horizon.x_pos = x_pos; instrument->instr.horizon.y_pos = y_pos; instrument->instr.horizon.scr_width = length; instrument->instr.horizon.scr_hole = hole_len; instrument->instr.horizon.load_value = load_value; instrument->next = tmp_first; hud->instruments = instrument; return( hud ); } Hptr fgHUDAddScale( Hptr hud, int type, int scr_pos, int scr_min, int scr_max, int div_min, int div_max, \ int orientation, int with_min, int min_value, int width_units, double (*load_value)() ) { struct HUD_scale *scale; struct HUD_instr *instrument; HIptr tmp_first, tmp_next; tmp_first = hud->instruments; if( tmp_first != NULL ) tmp_next = tmp_first->next; else tmp_next = NULL; instrument = (HIptr)calloc(sizeof(struct HUD_instr),1); if( instrument == NULL ) return( NULL ); scale = (struct HUD_scale *)calloc(sizeof(struct HUD_scale),1); if( scale == NULL ) return( NULL ); instrument->type = SCALE; instrument->instr = *scale; instrument->instr.scale.type = type; instrument->instr.scale.scr_pos = scr_pos; instrument->instr.scale.scr_min = scr_min; instrument->instr.scale.scr_max = scr_max; instrument->instr.scale.div_min = div_min; instrument->instr.scale.div_max = div_max; instrument->instr.scale.orientation = orientation; instrument->instr.scale.with_minimum = with_min; instrument->instr.scale.minimum_value = min_value; instrument->instr.scale.width_units = width_units; instrument->instr.scale.load_value = load_value; instrument->next = tmp_first; hud->instruments = instrument; return( hud ); } Hptr fgHUDAddLabel( Hptr hud, int x_pos, int y_pos, int size, int blink, int justify, \ char *pre_str, char *post_str, char *format, double (*load_value)() ) { struct HUD_label *label; struct HUD_instr *instrument; HIptr tmp_first, tmp_next; tmp_first = hud->instruments; if( tmp_first != NULL ) tmp_next = tmp_first->next; else tmp_next = NULL; instrument = (HIptr)calloc(sizeof(struct HUD_instr),1); if( instrument == NULL ) return( NULL ); label = (struct HUD_label *)calloc(sizeof(struct HUD_label),1); if( label == NULL ) return( NULL ); instrument->type = LABEL; instrument->instr = *label; instrument->instr.label.x_pos = x_pos; instrument->instr.label.y_pos = y_pos; instrument->instr.label.size = size; instrument->instr.label.blink = blink; instrument->instr.label.justify = justify; instrument->instr.label.pre_str = pre_str; instrument->instr.label.post_str = post_str; instrument->instr.label.format = format; instrument->instr.label.load_value = load_value; instrument->next = tmp_first; hud->instruments = instrument; return( hud ); } Hptr fgHUDAddLadder( Hptr hud, int x_pos, int y_pos, int scr_width, int scr_height, \ int hole_len, int div_units, int label_pos, int width_units, \ double (*load_roll)(), double (*load_pitch)() ) { struct HUD_ladder *ladder; struct HUD_instr *instrument; HIptr tmp_first, tmp_next; tmp_first = hud->instruments; if( tmp_first != NULL ) tmp_next = tmp_first->next; else tmp_next = NULL; instrument = (HIptr)calloc(sizeof(struct HUD_instr),1); if( instrument == NULL ) return( NULL ); ladder = (struct HUD_ladder *)calloc(sizeof(struct HUD_ladder),1); if( ladder == NULL ) return( NULL ); instrument->type = LADDER; instrument->instr = *ladder; instrument->instr.ladder.type = 0; // Not used. instrument->instr.ladder.x_pos = x_pos; instrument->instr.ladder.y_pos = y_pos; instrument->instr.ladder.scr_width = scr_width; instrument->instr.ladder.scr_height = scr_height; instrument->instr.ladder.scr_hole = hole_len; instrument->instr.ladder.div_units = div_units; instrument->instr.ladder.label_position = label_pos; instrument->instr.ladder.width_units = width_units; instrument->instr.ladder.load_roll = load_roll; instrument->instr.ladder.load_pitch = load_pitch; instrument->next = tmp_first; hud->instruments = instrument; return( hud ); } /* Hptr fgHUDAddMovingHorizon( Hptr hud, int x_pos, int y_pos, int length, int hole_len, \ int color ) { } Hptr fgHUDAddCircularLadder( Hptr hud, int scr_min, int scr_max, int div_min, int div_max, \ int max_value ) { } Hptr fgHUDAddNumDisp( Hptr hud, int x_pos, int y_pos, int size, int color, int blink, \ char *pre_str, char *post_str ) { } */ void fgUpdateHUD( Hptr hud ) { HIptr hud_instr; union HUD_instr_data instr_data; glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, 640, 0, 480); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glColor3f(1.0, 1.0, 1.0); glIndexi(7); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glLineWidth(1); glColor3f (0.1, 0.9, 0.1); #ifdef DEBUG printf( "HUD Code %d Status %d\n", hud->code, hud->status ); #endif hud_instr = hud->instruments; while( hud_instr != NULL ) { instr_data = hud_instr->instr; #ifdef DEBUG printf("Instr Type %d SubType %d Orient %d\n", hud_instr->type, hud_instr->sub_type, hud_instr->orientation ); #endif if( hud_instr->type == ARTIFICIAL_HORIZON ) { drawhorizon( instr_data.horizon ); /* drawhorizon( instr_data.horizon.x_pos, instr_data.horizon.y_pos, \ instr_data.horizon.scr_width, instr_data.horizon.scr_hole ); */ } else if( hud_instr->type == SCALE ) { drawscale( instr_data.scale.type, instr_data.scale.with_minimum, \ instr_data.scale.minimum_value, instr_data.scale.orientation, \ instr_data.scale.scr_pos, instr_data.scale.scr_min, \ instr_data.scale.scr_max, instr_data.scale.width_units, \ instr_data.scale.div_min, instr_data.scale.div_max, \ (*instr_data.scale.load_value)() ); } else if( hud_instr->type == LABEL ) { drawlabel( instr_data.label ); /* drawlabel( instr_data.label.x_pos, instr_data.label.y_pos, instr_data.label.size, \ instr_data.label.blink, instr_data.label.pre_str, instr_data.label.post_str, \ instr_data.label.format, (*instr_data.label.load_value)() ); */ } else if( hud_instr->type == LADDER ) { drawladder( instr_data.ladder ); } hud_instr = hud_instr->next; } glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } /* $Log$ /* Revision 1.3 1997/09/05 14:17:26 curt /* More tweaking with stars. /* * Revision 1.2 1997/09/04 02:17:30 curt * Shufflin' stuff. * * Revision 1.1 1997/08/29 18:03:22 curt * Initial revision. * */