home *** CD-ROM | disk | FTP | other *** search
- // DRAW.CPP - the system-independent graphics portion of RARS 0.39
- // (was GRAPHICS.CPP) - by Mitchell E. Timin, State College, PA
- // See GI.H, CAR.H & TRACK.H for class and structure declarations.
- // This version is for Borland C++, version 3.1, and is for DOS.
- // This is part of version 0.60 of RARS (Robot Auto Racing Simulation).
- // GI.CPP is the system-dependent graphics portion of RARS.
- // ver. 0.1 release January 12, 1995
- // ver. 0.2 1/23/95
- // ver. 0.3 2/7/95
- // ver. 0.39 3/6/95
- // ver. 0.45 3/21/95
- // ver. 0.50 4/5/95
- // ver. 0.6b 5/8/95 b for beta
-
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include "car.h"
- #include "track.h"
- #include "os.h"
-
- static double finish_x, finish_y; // These four variables are used to
- static double finish_y_in, finish_y_out; // locate the finish line on the screen.
- static double spacing; // see leaders()
- static segment *trackout; // global variables that describe the track:
- static segment *trackin;
- static int NSEG;
- static double width;
-
- extern Car* pcar[]; // array of pointers to the various cars
- extern double length; // total lenth of track (smaller of inner and outer rails)
- extern car_ID drivers[]; // array of pointers to their name strings
- extern double CHR_HGT; // height in feet of row of text
- extern int no_display; // when set, do no graphics
-
- /* add prototypes which should have been there all along */
- void draw_arc(double, double, double, double, double);
-
- // These six functions are member functions of the Car class.
- // All they do is fetch private data from the car objects.
- inline double Car::get_speed(void)
- { return sqrt(xdot * xdot + ydot * ydot); }
-
- inline double Car::get_vc(void)
- { return vc; }
-
- inline double Car::get_alpha(void)
- { return alpha; }
-
- inline double Car::get_power(void)
- { return power; }
-
- inline double Car::get_lin_acc(void)
- { return tan_a/g; }
-
- inline double Car::get_lat_acc(void)
- { return cen_a/g; }
-
- int round(double given) // convert double to int by rounding
- {
- if(given > 0.0)
- return int(given + .5);
- else
- return int(given - .5);
- }
-
- // Convert input into an ASCII string with two decimal digits.
- void make_dec_string(char* out, // pointer to destination string
- double input) // value to be converted
- {
- long int value;
- char intpart[5], decpart[4];
- int neg = 0; // to flag negative numbers
-
- if(input < 0) {
- neg = 1;
- input = -input;
- }
-
- value = (long int)(100.0 * input + .5);
- itoa(int(value / 100), intpart, 10);
- itoa(int(value % 100), decpart, 10);
- if(decpart[1] == '\0') { // we might have to stick in a leading zero:
- decpart[2] = '\0';
- decpart[1] = decpart[0];
- decpart[0] = '0';
- }
- if(neg) { // we might have to put a minus sign in front:
- strcpy(out, "-");
- strcat(out, intpart);
- }
- else
- strcpy(out, intpart);
- strcat(out, ".");
- strcat(out, decpart);
- }
-
- // Assemble a string for the average speed of car i. (goes to char* out)
- void get_avg_spd(int i, char* out)
- {
- make_dec_string(out, pcar[i]->speed_avg * MPH_FPS);
- }
-
- // Assemble a string for the maximum speed of car i. (goes to char* out)
- void get_max_spd(int i, char* out)
- {
- make_dec_string(out, pcar[i]->speed_max * MPH_FPS);
- }
-
- // Draws the path specified by the segment array and starting
- // conditions which are given as parameters. Also, fills in the
- // un-initialized portions of the segment array. Returns the length.
- double drawpath(double xstart, // coordinates of starting point
- double ystart,
- double alfstart, // starting tangent angle
- segment *track) // pointer to structure that defines path
- {
- double length = 0; // to accumulate total length of path
- double cenx, ceny; // center of circle arc
- double radius; // radius of circle arc (negative == rt. turn)
- double x, y, alf; // position and direction of start of segment
- double newx, newy, newalf; // and the one after that (alf in radians)
- int i;
-
- x = xstart; y = ystart; // store starting point & direction
- alf = alfstart;
-
- for(i=0; i < NSEG; i++) { // for each segment:
- radius = track[i].radius;
- if(radius == 0.0) { // is this a straightaway?
- length += track[i].length;
- newx = x + track[i].length * cos(alf); // find end coordinates
- newy = y + track[i].length * sin(alf);
- track[i].end_x = newx; track[i].end_y = newy; // fill in these
- track[i].beg_x = x; track[i].beg_y = y; // empty slots in
- track[i].beg_ang = track[i].end_ang = alf; // the track array
- newalf = alf; // direction won't change
- if(!no_display)
- draw_line(x, y, newx, newy); // draw the straight line
- if(i == 0) { // find pixel locations of start/finish line:
- finish_y = newy; // assume straightaway parallel to x-axis
- finish_x = x + FINISH * length;
- }
- }
- else if(radius > 0.0) {
- length += radius * track[i].length;
- cenx = x - radius * sin(alf); // compute center location:
- ceny = y + radius * cos(alf);
- track[i].cen_x = cenx; track[i].cen_y = ceny; // fill empty slots
- track[i].beg_ang = alf;
- newalf = alf + track[i].length; // compute new direction
- if(newalf > 2.0 * PI)
- newalf -= 2.0 * PI;
- track[i].end_ang = newalf; // fill this empty slot
- newx = cenx + radius * sin(newalf); // location of end
- newy = ceny - radius * cos(newalf);
- track[i].end_x = newx; track[i].end_y = newy; // fill in these
- track[i].beg_x = x; track[i].beg_y = y; // empty slots
- if(!no_display)
- draw_arc(radius, cenx, ceny, alf, track[i].length); // draw the arc
- }
- else {
- length -= radius * track[i].length;
- cenx = x - radius * sin(alf); // compute center location:
- ceny = y + radius * cos(alf);
- track[i].cen_x = cenx; track[i].cen_y = ceny; // fill empty slots
- track[i].beg_ang = alf;
- newalf = alf - track[i].length; // compute new direction
- if(newalf < -2.0 * PI)
- newalf += 2.0 * PI;
- track[i].end_ang = newalf; // fill this empty slot
- newx = cenx + radius * sin(newalf); // location of end
- newy = ceny - radius * cos(newalf);
- track[i].end_x = newx; track[i].end_y = newy; // fill in these
- track[i].beg_x = x; track[i].beg_y = y; // empty slots
- if(!no_display)
- draw_arc(radius, cenx, ceny, alf, track[i].length); // draw the arc
- }
- x = newx; // repeat with new position and direction:
- y = newy;
- alf = newalf;
- }
- // To close the circuit, draw a line from the last point back to the first.
- // This usually is not necessary, but it prevents flood fill leaking.
- if (!no_display)
- draw_line(x, y, xstart, ystart);
-
- return length; // return the length of the path
- }
-
- // Draw a little car on the screen, at given position and orientation,
- // and with the given colors. (to erase the car, call it with track_color)
- void drawcar(double x, // coordinates of center of car
- double y,
- double ang, // orientation angle of car, wrt x-axis, radians
- int nose, // color of front portion
- int tail) // color of rear portion
- {
- double xx, yy, endx, endy;
- double sine, cosine, dx, dy;
- int i;
-
- sine = sin(ang); cosine = cos(ang);
- xx = x + cosine * CARLEN/2 - sine * CARWID/2; // left front corner coords
- yy = y + cosine * CARWID/2 + sine * CARLEN/2;
- x = xx; y = yy; // save the above values
- dx = 0.3333 * CARWID * sine;
- dy = -.3333 * CARWID * cosine;
- // below we draw four parallel lines to form the body of the car:
- set_color(tail);
- for(i=0; i<=3; i++) {
- endx = xx - CARLEN * cosine;
- endy = yy - CARLEN * sine;
- draw_line(xx, yy, endx, endy);
- if(i == 3) break;
- xx += dx;
- yy += dy;
- }
- // now four short lines of the nose color to decorate the front:
- set_color(nose);
- xx = x; yy = y; // restore x and y to left front of car
- for(i=0; i<=3; i++) {
- endx = xx + CARWID * sine;
- endy = yy - CARWID * cosine;
- draw_line(xx, yy, endx, endy);
- if(i == 3) break;
- xx += dy;
- yy -= dx;
- }
- }
-
- void lapper(int which, int lap) // shows lap count on scoreboard,
- { // also returns lap+1 to advance the lap count
- char string[] = " ";
-
- if(lap < 0)
- lap = 0;
-
- set_fill_color(FIELD_COLOR); // the green part is off the track
- // This bar erases the previous lap count:
- rectangle(SCORE_BOARD_X + 10*CHR_WID,
- SCORE_BOARD_Y - which*CHR_HGT,
- SCORE_BOARD_X + 13*CHR_WID,
- SCORE_BOARD_Y - (which+.9)*CHR_HGT);
-
- set_color(TEXT_COLOR); // now print text in black:
- itoa(lap, string, 10);
- text_output(SCORE_BOARD_X + 10 * CHR_WID,
- SCORE_BOARD_Y - which * CHR_HGT, string);
- }
-
- void designate(int i) // marks car i on scoreboard, with a ">"
- {
- static int iwas = -1; // the previous i, for erasing
-
- set_fill_color(FIELD_COLOR);
- // This small rectangle erases the previous designator:
- if(iwas >= 0)
- rectangle(SCORE_BOARD_X - .7 * CARLEN - CHR_WID,
- SCORE_BOARD_Y - iwas * CHR_HGT,
- SCORE_BOARD_X - .7 * CARLEN -.2 * CHR_WID,
- SCORE_BOARD_Y - (iwas + .9) * CHR_HGT);
-
- // now print the new designator
- if(i >= 0) {
- set_color(TEXT_COLOR);
- text_output(SCORE_BOARD_X - .7 * CARLEN - CHR_WID,
- SCORE_BOARD_Y - i * CHR_HGT, ">");
- }
- else if(iwas > -1) { // if no robot is designated, then erase the IP
- rectangle(IP_X, IP_Y, IP_X + 21 * CHR_WID, IP_Y - CHR_HGT); // erase name
- rectangle(IP_X, IP_Y - CHR_HGT, // erase data
- IP_X + 17.5 * CHR_WID, IP_Y - 7 * CHR_HGT);
- }
-
- iwas = i;
- }
-
- void border(void) // draws a thin border around the entire screen
- {
- set_color(RAIL_COLOR);
- draw_line(0.0, 0.0, X_MAX, 0.0);
- draw_line(X_MAX, 0.0, X_MAX, Y_MAX);
- draw_line(X_MAX, Y_MAX, 0.0, Y_MAX);
- draw_line(0.0, Y_MAX, 0.0, 0.0);
- }
-
- // Initializes graphics system, draws track, fills in colored regions:
- void graph_setup(void)
- {
- double alt_len; // used in deciding the length of the track
-
- build_track(); // read track data and fill in trackout[], trackin[]
-
- // get track information into our file scope global variables:
- NSEG = get_track_description().NSEG;
- width = get_track_description().width;
- trackin = get_track_description().trackin;
- trackout = get_track_description().trackout;
-
- if(!no_display) {
- initialize_graphics();
- // paint the whole screen green:
- set_fill_color(FIELD_COLOR);
- rectangle(0.0, Y_MAX, X_MAX, 0.0);
- border(); // draw border at screen boundary
- }
-
- // draw outer track boundary:
- length = drawpath(TRK_STRT_X, TRK_STRT_Y, 0, trackout);
- finish_y_out = finish_y; // locate one end of finish line
- // draw inner track boundary:
- alt_len = drawpath(TRK_STRT_X, TRK_STRT_Y+width, 0, trackin);
- if(alt_len < length) // take length of shorter rail as track length
- length = alt_len;
- finish_y_in = finish_y; // locate other end of finish line
- if(no_display)
- return;
- // pave the track:
- set_fill_color(TRACK_COLOR);
- flood_fill(TRK_STRT_X, TRK_STRT_Y + width/2); // color the track
- }
-
- void refresh_finish_line() // re-draw the finish line:
- {
- set_color(TEXT_COLOR);
- draw_line(finish_x, finish_y_out, finish_x, finish_y_in);
- }
-
- // Put up the scoreboard:
- void scoreboard(STAGE stage)
- {
- int i;
-
- double XS = SCORE_BOARD_X;
- double YS = SCORE_BOARD_Y;
- char string[] = "0123456789ABCD";
- int kount;
-
- spacing = 1.15 * CHR_HGT; // for the leader board only
- // these rectangles are for the leader board car pictures:
- kount = car_count < 5 ? car_count : 5;
- // first erase the old board, if any:
- set_fill_color(FIELD_COLOR);
- rectangle(LDR_BRD_X - 1.9 * CARLEN, LDR_BRD_Y - CHR_HGT,
- LDR_BRD_X + 19*CHR_WID, LDR_BRD_Y - (kount + .7) * spacing);
- set_fill_color(TRACK_COLOR); // rectangular background for car pictures:
- rectangle(LDR_BRD_X - 1.9 * CARLEN, LDR_BRD_Y - CHR_HGT,
- LDR_BRD_X -.5 * CARLEN, LDR_BRD_Y - (kount + .7) * spacing);
- // these rectangles are for the scoreboard car pictures:
- // first erase the old board, if any:
- set_fill_color(FIELD_COLOR);
- rectangle(XS-.7*CARLEN, YS+CHR_HGT/2, XS+13*CHR_WID,
- YS - car_count * CHR_HGT);
- set_fill_color(TRACK_COLOR);
- rectangle(XS-.7*CARLEN, YS+CHR_HGT/2, XS+.5*CARLEN,
- YS - car_count * CHR_HGT);
- // draw the cars on the scoreboard:
- for(i=0; i<car_count; i++) {
- drawcar(XS-.1*CARLEN, YS - i * CHR_HGT - CARWID/2, 0,
- drivers[i].paint_job.nose, drivers[i].paint_job.tail);
- set_color(TEXT_COLOR);
- itoa(lap_count, string, 10);
- text_output(XS+2*CHR_WID, YS - i * CHR_HGT, drivers[i].rob_name);
- (void)lapper(i,-1);
- }
- // erase either "Practice" or "Race Length", and number of laps:
- rectangle(XS - 13 * CHR_WID, SCORE_BOARD_Y + 1.5 * CHR_HGT,
- XS - 3 * CHR_WID, SCORE_BOARD_Y - 1.5 * CHR_HGT);
- text_output(XS - 13 * CHR_WID, SCORE_BOARD_Y + 1.5 * CHR_HGT,
- stage == PRACTICE ? " Practice Car Driver Laps"
- : "Race Length Car Driver Laps");
- text_output(LOTIX, LOTIY, "track length mi.");
- make_dec_string(string, length/5280.0);
- text_output(LOTIX + 11.5 * CHR_WID, LOTIY, string);
- // Show the name of the track:
- text_output(LOTIX, LOTIY + CHR_HGT, "track is");
- for(i=0; trackfile[i]; i++)
- if(trackfile[i] == '.' || trackfile[i] == 0)
- break;
- else
- string[i] = trackfile[i];
- string[i] = 0;
- text_output(LOTIX + 8 * CHR_WID, LOTIY + CHR_HGT, string);
-
- itoa(stage == PRACTICE ? practice : lap_count, string, 10);
- text_output(SCORE_BOARD_X-10*CHR_WID, SCORE_BOARD_Y, string);
- text_output(SCORE_BOARD_X-7*CHR_WID, SCORE_BOARD_Y, "laps");
- text_output(LDR_BRD_X - CHR_WID, LDR_BRD_Y, "LEADERS: max avg");
- text_output(LDR_BRD_X+11.5*CHR_WID, LDR_BRD_Y+CHR_HGT, "mph");
- }
-
- // update the leader board when necessary:
- void leaders(int i, int* order)
- {
- char string[] = "0123456789";
- double Y;
-
- Y = LDR_BRD_Y - spacing * (i + 1);
-
- // Erase old text:
- set_fill_color(FIELD_COLOR); // The infield color
- rectangle(LDR_BRD_X, Y, LDR_BRD_X+19*CHR_WID, Y - CHR_HGT);
-
- set_color(TEXT_COLOR);
- text_output(LDR_BRD_X, Y, drivers[order[i]].rob_name);
- get_max_spd(order[i], string); // the maximum speed:
- text_output(LDR_BRD_X+7.5*CHR_WID, Y, string);
- get_avg_spd(order[i], string); // the average speed:
- text_output(LDR_BRD_X+13.5*CHR_WID, Y, string);
- drawcar(LDR_BRD_X-1.2*CARLEN, Y - CARWID/2, 0.0,
- drivers[order[i]].paint_job.nose, drivers[order[i]].paint_job.tail);
- }
-
- void instruments(int i) // Instrument Panel for car i
- {
- char out[16];
- static int alpha_up = 0; // set when the descriptions are printed
-
- set_fill_color(FIELD_COLOR); // The infield color
- rectangle(IP_X, IP_Y, IP_X + 21 * CHR_WID, IP_Y - CHR_HGT); // erase name
-
- if(i < 0 || i >= car_count) { // Do nothing if i is invalid.
- alpha_up = 0;
- return;
- }
-
- if(!alpha_up) {
- set_color(TEXT_COLOR);
- text_output(IP_X, IP_Y - CHR_HGT, "speedometer");
- text_output(IP_X, IP_Y - 2 * CHR_HGT, "true speed");
- text_output(IP_X, IP_Y - 3 * CHR_HGT, "lateral g's");
- text_output(IP_X, IP_Y - 4 * CHR_HGT, "in-line g's");
- text_output(IP_X, IP_Y - 5 * CHR_HGT, "skid angle");
- text_output(IP_X, IP_Y - 6 * CHR_HGT, "power, pct.");
- alpha_up = 1;
- }
-
- set_color(IP_NAME_COLOR);
- text_output(IP_X, IP_Y, drivers[i].rob_name);
- text_output(IP_X + (strlen(drivers[i].rob_name)-.5) * CHR_WID, IP_Y, "'s instruments:");
-
- set_color(IP_NUM_COLOR);
- rectangle(IP_X + 11 * CHR_WID, IP_Y - CHR_HGT,
- IP_X + 17 * CHR_WID, IP_Y - 2 * CHR_HGT);
- make_dec_string(out, pcar[i]->get_vc() * MPH_FPS);
- text_output(IP_X + 11 * CHR_WID, IP_Y - CHR_HGT, out);
-
- rectangle(IP_X + 11 * CHR_WID, IP_Y - 2 * CHR_HGT,
- IP_X + 17 * CHR_WID, IP_Y - 3 * CHR_HGT);
- make_dec_string(out, pcar[i]->get_speed() * MPH_FPS);
- text_output(IP_X + 11 * CHR_WID, IP_Y - 2 * CHR_HGT, out);
-
- rectangle(IP_X + 11 * CHR_WID, IP_Y - 3 * CHR_HGT,
- IP_X + 17 * CHR_WID, IP_Y - 4 * CHR_HGT);
- make_dec_string(out, pcar[i]->get_lat_acc());
- text_output(IP_X + 11 * CHR_WID, IP_Y - 3 * CHR_HGT, out);
-
- rectangle(IP_X + 11 * CHR_WID, IP_Y - 4 * CHR_HGT,
- IP_X + 17 * CHR_WID, IP_Y - 5 * CHR_HGT);
- make_dec_string(out, pcar[i]->get_lin_acc());
- text_output(IP_X + 11 * CHR_WID, IP_Y - 4 * CHR_HGT, out);
-
- rectangle(IP_X + 11 * CHR_WID, IP_Y - 5 * CHR_HGT,
- IP_X + 17 * CHR_WID, IP_Y - 6 * CHR_HGT);
- make_dec_string(out, pcar[i]->get_alpha() * DEGPRAD);
- text_output(IP_X + 11 * CHR_WID, IP_Y - 5 * CHR_HGT, out);
-
- rectangle(IP_X + 11 * CHR_WID, IP_Y - 6 * CHR_HGT,
- IP_X + 17 * CHR_WID, IP_Y - 7 * CHR_HGT);
- make_dec_string(out, pcar[i]->get_power() * 100.0);
- text_output(IP_X + 11 * CHR_WID, IP_Y - 6 * CHR_HGT, out);
- }
-
- /* new routine: draw_arc() routine that uses draw_line() to draw the arc */
- #define LINESEG_LENGTH 07 /* this constant can be used to tweak the precision */
- void draw_arc(double radius, double center_x, double center_y, double start_angle, double length)
- {
- double a;
- double stepsize;
- double x1, y1, x2, y2;
-
- /* convert a right turn so it is consistent with the left turn */
- if (radius < 0.0)
- {
- radius = -radius;
-
- start_angle = start_angle - length - PI;
- while (start_angle < 0.0)
- {
- start_angle += (2 * PI);
- }
- }
-
- /* calculate the starting point */
- x1 = center_x + radius * sin(start_angle);
- y1 = center_y - radius * cos(start_angle);
-
- /* determine the step size */
- stepsize = LINESEG_LENGTH * (1.0 / (radius * SCALE));
-
- /* draw lines over the length from there, adapting the number of steps to the length */
- for (a = stepsize; a < length; a += stepsize)
- {
- /* calculate the end point of this line */
- x2 = center_x + radius * sin(start_angle + a);
- y2 = center_y - radius * cos(start_angle + a);
-
- /* draw the line */
- draw_line(x1, y1, x2, y2);
-
- /* make the end point the new starting point for the next line */
- x1 = x2;
- y1 = y2;
- }
-
- /* calculate the end point of the arc */
- x2 = center_x + radius * sin(start_angle + length);
- y2 = center_y - radius * cos(start_angle + length);
-
- /* draw the last line */
- draw_line(x1, y1, x2, y2);
- }
-
-