// "Graced"
// Graphical trace editor for Grand Prix Legends maps
// Copyright Anton Norup Sørensen, 2002-3
// This program is distributed under the GNU General Public License
// for free software. Read the included license file for further details.
// In 2011 Bernd Nowak changed the display stuff for Allegro 4.4 version
// Reason for the change was compability for Vista and Windows 7
// Also was the H/h key added to give a short overview of the available commands
// Another added feature was the ability to use PCX font files but this might give
// unwanted results if size is over 10 pixels

#define VERSION "0.99"

#define MAXNSECTIONS 4096
#define MAXNTRACES 16

#define TICKSPERMETER 19685.0
#define PI 3.14159265

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <allegro.h>

typedef short bool;
#define false ((bool)0)
#define true  ((bool)1)

#define BLACK makecol( 0,   0,   0   )
#define BLUE makecol( 0,   0,   255 )
#define GREEN makecol( 0,   255, 0   )
#define CYAN makecol( 0,   255, 255 )
#define RED makecol( 255, 0,   0   )
#define MAGENTA makecol( 255, 0,   255 )
#define BROWN makecol( 139, 69, 19 )
#define LIGHTGRAY makecol( 211, 211, 211 )
#define DARKGREY makecol( 84, 84, 84 )
#define LIGHTBLUE makecol( 173, 216, 230 )
#define LIGHTGREEN makecol( 144, 238, 144 )
#define LIGHTCYAN makecol( 224, 255, 255   )
#define LIGHTRED makecol( 255, 51, 51   )
#define LIGHTMAGENTA makecol( 255, 128, 255 )
#define YELLOW makecol( 255, 255, 0   )
#define WHITE makecol( 255, 255, 255 )


DATAFILE *data;
FONT *font_big;
FONT *font_med;
FONT *font_sml;
FONT *addFont;
FONT *usedTextFont;
FONT *addSFont;
FONT *addMFont;
FONT *addBFont;
FONT *usedSTextFont;
FONT *usedMTextFont;
FONT *usedBTextFont;

int nsections, ntraces;
int seclength[MAXNSECTIONS];
int secbeg[MAXNSECTIONS];
int trace_poly[MAXNSECTIONS][MAXNTRACES][4];
float tracepos[MAXNTRACES];
float radius[MAXNSECTIONS];
bool using_radius = false;
int radseclength[MAXNSECTIONS][MAXNTRACES];
int radsecbeg[MAXNSECTIONS][MAXNTRACES];
float secbegmeter[MAXNSECTIONS];
float radsecbegmeter[MAXNSECTIONS][MAXNTRACES];

#define MAXNALTREF 4096
float refpos[MAXNALTREF];
float refalt[MAXNALTREF];
int naltref=0;
bool using_altref = false;
float altrefoffset = 0.;

char infile[256];
char outfile[256];
char trkfile[256];
char traceposfile[256];
char radiusfile[256];
char altreffile[256];
char fontfile[256];
char sfontfile[256];
char mfontfile[256];
char bfontfile[256];

bool quit = false;
bool changed = false;
bool show_angles = true;

int start_sec, end_sec, cross_sec;
int start_trace, end_trace;

float minh, maxh;

int centreline_id, centreline_override, trace_id, secdivs;

int twin_xbeg, twin_ybeg, twin_xsize, twin_ysize;
float tw_dat_xmin, tw_dat_xmax, tw_dat_ymin, tw_dat_ymax;

int cwin_xbeg, cwin_ybeg, cwin_xsize, cwin_ysize;
float cw_dat_xmin, cw_dat_xmax, cw_dat_ymin, cw_dat_ymax;

bool mbtn1_press = false;

int showheight_last_y = 0;

PALETTE grapal;

int scx = 640;
int scy = 480;

BITMAP *buffer;

void update_screen(void)
{
    show_mouse(NULL);
	blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); // blit the buffer to the screen
	show_mouse(screen);
}

int init_double_buffering()
{
	// create the memory buffer
	buffer = create_bitmap(SCREEN_W, SCREEN_H);

	if(!buffer)
		return FALSE;

	return TRUE;
}

void help()
{
    rectfill(screen, 2,5,450,160,BLUE);
    textprintf(screen, usedTextFont, 4, 10, WHITE, "w: Write traces to file");
    textprintf(screen, usedTextFont, 4, 20, WHITE, "a: Toggle display of angles");
    textprintf(screen, usedTextFont, 4, 30, WHITE, "c: Check altitude and gradient jumps");
    textprintf(screen, usedTextFont, 4, 40, WHITE, "f: Fix altitude and gradient jumps");
    textprintf(screen, usedTextFont, 4, 50, WHITE, "z: Zoom out to show entire track");
    textprintf(screen, usedTextFont, 4, 60, WHITE, "q: Quit");
    textprintf(screen, usedTextFont, 4, 70, WHITE, "Q: Quit without saving");
    textprintf(screen, usedTextFont, 4, 80, WHITE, "Cursor up/down: Select trace");
    textprintf(screen, usedTextFont, 4, 90, WHITE, "Home: Select centre-line trace");
    textprintf(screen, usedTextFont, 4, 100, WHITE, "KP 7/1: Select leftmost section or trace (*)");
    textprintf(screen, usedTextFont, 4, 110, WHITE, "KP 9/3: Select rightmost section or trace (*)");
    textprintf(screen, usedTextFont, 4, 120, WHITE, "Insert/Delete: Zoom upper height limit");
    textprintf(screen, usedTextFont, 4, 130, WHITE, "Page Up/Down: Zoom lower height limit");
    textprintf(screen, usedTextFont, 4, 140, WHITE, "Left mouse btn: Select section or edit trace");
    textprintf(screen, usedTextFont, 4, 150, WHITE, "Right mouse btn: Cancel edit");
	rest(4000);
}



void intro(void)
{
 char text[128];

 strcpy(text, "Graced v. ");
 printf("%s", text);
 strcpy(text, VERSION);
 printf("%s\n", text);
}



void set_defaults(void)
{
 int i;

 for (i=0; i<MAXNTRACES; i++) tracepos[i]=i;

 strcpy(infile , "");
 strcpy(outfile, "");
 strcpy(trkfile, "");
 strcpy(traceposfile, "tracepos.txt");
 strcpy(radiusfile, "radius.txt");
 strcpy(altreffile, "altref.txt");
}



int load_config(void)
{
 FILE *fp;
 char *token;
 char buf[256];

 if ((fp=fopen("graced.cfg","r")) == NULL) {
  printf("Fatal error: Did not find >graced.cfg<\n");
  exit(1);
 }

 printf("Reading config file\n");

 while ( feof(fp) == 0 )
 {
   fgets(buf,240,fp);
   token = strtok(buf," \n");
   if ( token == NULL )
      {;}
   else if ( strcmp(token,"INFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(infile, token);
   }
   else if ( strcmp(token,"OUTFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(outfile, token);
   }
   else if ( strcmp(token,"TRKFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(trkfile, token);
   }
   else if ( strcmp(token,"TRACEPOSFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(traceposfile, token);
   }
   else if ( strcmp(token,"RADIUSFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(radiusfile, token);
   }
   else if ( strcmp(token,"ALTREFFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(altreffile, token);
   }
   else if ( strcmp(token,"ALTREFOFFSET") == 0 )
   {
      token = strtok(NULL," \n");
      altrefoffset = atof(token);
   }
   else if ( strcmp(token,"CENTRELINE") == 0 )
   {
      token = strtok(NULL," \n");
      centreline_override = atoi(token);
   }
   else if ( strcmp(token,"SHOWANGLES") == 0 )
   {
      token = strtok(NULL," \n");
      show_angles = atoi(token);
   }
   else if ( strcmp(token,"GRAPHRES") == 0 )
   {
      token = strtok(NULL," \n");
      scx = atoi(token);
      token = strtok(NULL," \n");
      scy = atoi(token);
   }
   else if ( strcmp(token,"FONTFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(fontfile, token);
   }
   else if ( strcmp(token,"SFONTFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(sfontfile, token);
   }
   else if ( strcmp(token,"MFONTFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(mfontfile, token);
   }
   else if ( strcmp(token,"BFONTFILE") == 0 )
   {
      token = strtok(NULL," \n");
      strcpy(bfontfile, token);
   }

 }

 fclose(fp);

 return(0);
}



int load_traces(void)
{
 FILE *fp;
 int i, j;
 int dummy = 0;
 float fdummy = 0.0;

 if ((fp=fopen(infile,"r")) == NULL) {
  printf("Fatal error: Did not find >%s<\n", infile);
  exit(1);
 }

 printf("Reading trace data from >%s<\n", infile);

 i=0;
 fscanf(fp,"%d,", &nsections);  // Read the number of sections
 // Format allows for both comma and space separation

 fscanf(fp,"%d,", &ntraces); // Read the number of traces

 for (i=0; i<80; i++) fscanf(fp,"%d,", &dummy); // Skip zeroes

 for (i=0; i<nsections; i++) {  // For each section
   fscanf(fp,"%d,", &dummy);      // Read unused id
   fscanf(fp,"%d,", &seclength[i]);     // Read section length
   for (j=0; j<MAXNTRACES; j++) {        // For each trace
    fscanf(fp,"%d,", &trace_poly[i][j][0]);
    fscanf(fp,"%d,", &trace_poly[i][j][3]);
    fscanf(fp,"%d,", &trace_poly[i][j][2]);
    fscanf(fp,"%d,", &trace_poly[i][j][1]);
    fscanf(fp,"%f,", &fdummy); // Skip unused
   }
   if (i > 0) secbeg[i]=secbeg[i-1]+seclength[i-1];
 }

 fclose(fp);

 return(0);
}



int save_traces(void)
{
 FILE *fp;
 char string[256];
 int i, j;

 if ((fp=fopen(outfile,"w")) == NULL) {
  printf("Warning: Could not save to >%s<\n", outfile);
  return(1);
 }

 textprintf(screen, usedTextFont, 4, 10, WHITE, "Saving trace data to >%s<", outfile);

 sprintf(string, " %i %i", nsections, ntraces);
 fprintf(fp, string);

 for (i=0; i<80; i++) {
  sprintf(string, " %i", 0);
  fprintf(fp, string);
 }
 sprintf(string, "\n");
 fprintf(fp, string);

 for (i=0; i<nsections; i++) {  // For each section
  sprintf(string, " %i %i", i, (int)seclength[i]);
  fprintf(fp, string);
  for (j=0; j<16; j++) {        // For each trace
    sprintf(string, " %i", (int)trace_poly[i][j][0]);
    fprintf(fp, string);
    sprintf(string, " %i", (int)trace_poly[i][j][3]);
    fprintf(fp, string);
    sprintf(string, " %i", (int)trace_poly[i][j][2]);
    fprintf(fp, string);
    sprintf(string, " %i", (int)trace_poly[i][j][1]);
    fprintf(fp, string);
    sprintf(string, " %f", 0.0);
    fprintf(fp, string);
  }
  sprintf(string, "\n");
  fprintf(fp, string);
 }

 fclose(fp);

 changed = false;

 rest(500);

 return(0);
}



int load_tracepos(void)
{
 FILE *fp;
 char string[256];
 int i;

 if ((fp=fopen(traceposfile,"r")) == NULL) {
  printf("Did not find >%s<\n", traceposfile);
  return(1);
 }

 printf("Reading trace positions from >%s<\n", traceposfile);

 for (i=0; i<MAXNTRACES; i++) {
  fscanf(fp,"%s", string);
  tracepos[i] = atof(string);
 }

 fclose(fp);

 return(0);
}



void check_totturn(void)
{
 float totturn = 0.;
 float turnerr;
 int i;

 for (i=0; i<nsections; i++) {
  if (radius[i]!=0)
   totturn += seclength[i]/(radius[i]*TICKSPERMETER);
 }

 turnerr = fabs( (totturn/(2*PI)) - (int)(totturn/(2*PI)) )  * (180/PI);

 if (turnerr > 0.2) {
   printf("Caution: Possible error in radius data\n");
   printf("Missing %7.2f degrees to close track\n", turnerr);
 }
}



int load_radius(void)
{
 FILE *fp;
 char string[256];
 int i;

 if ((fp=fopen(radiusfile,"r")) == NULL)
  return(1);

 printf("Reading radius data from >%s<\n", radiusfile);

 for (i=0; i<nsections; i++) {
  fscanf(fp,"%s", string);
  fscanf(fp,"%s", string);
  radius[i] = atof(string);
 }

 fclose(fp);

 using_radius = true;

 check_totturn();

 return(0);
}



int scan_trk(void)
{
 FILE *fp;

 int i, val;

 if ((fp=fopen(trkfile,"rb")) == NULL) {
  printf("Did not find >%s<\n", trkfile);
  return(1);
 }

 printf("Reading trace-position and radius data from >%s<\n", trkfile);

 fseek( fp, 0x0C, SEEK_SET);
 val = getw(fp);
 if (val != ntraces) {
   printf("Warning: Number of traces in .txt and .trk does not match!\n");
 }
 fseek( fp, 0x10, SEEK_SET);
 val = getw(fp);
 if (val != nsections) {
   printf("Warning: Number of sections in .txt and .trk does not match!\n");
 }

 for (i=0; i < MAXNTRACES; i++) {
   fseek( fp, 28+4*i, SEEK_SET);
   val = getw(fp);
   tracepos[i] = (float)val/TICKSPERMETER;
 }

 for (i=0; i < nsections; i++) {
   int straight, curvature, secticklen;
   long adr = -(nsections * 52) + 52 * i;
   fseek( fp, adr, SEEK_END);
   straight = getc(fp);
   fseek( fp, adr+8, SEEK_END);
   secticklen = getw(fp);
   fseek( fp, adr+28, SEEK_END);
   curvature = getw(fp);
   if (straight==1) {
     radius[i]=0.0;
   }
   else {
     float secmeter = (float)secticklen/TICKSPERMETER;
     float fraccurv = (float)curvature/0x80000000;
     radius[i] = secmeter/(2.0*PI*fraccurv);
   }
 }

 fclose(fp);

 using_radius = true;

 check_totturn();

 return(0);
}



int load_altref(void)
{
 FILE *fp;
 char string[256];
 int i=0;

 if ((fp=fopen(altreffile,"r")) == NULL)
  return(1);

 printf("Reading altitude reference data from >%s<\n", altreffile);

 while(!feof(fp)) {
  fscanf(fp,"%s", string);
  refpos[i] = atof(string);
  fscanf(fp,"%s", string);
  refalt[i] = atof(string);
  refalt[i] += altrefoffset;
  i++;
 }

 naltref = i-1;

 fclose(fp);

 using_altref = true;

 return(0);
}


int setup_graph(void)
{
 int i;

 set_color_depth(desktop_color_depth());

 if (set_gfx_mode(GFX_AUTODETECT, scx,scy, 0,0))
 {
        allegro_message("Unable to set graphics mode.\n");
		exit(1);
 }


 set_display_switch_mode(SWITCH_PAUSE);

 if (!init_double_buffering())
 {
    set_gfx_mode(GFX_TEXT,0,0,0,0);
	allegro_message("Unable to initialize page flipping.\n");
	exit(1);
 }



 twin_xbeg  =  SCREEN_W * 0.05;
 twin_ybeg  =  SCREEN_H * 0.95;
 twin_xsize =  SCREEN_W * 0.90;
 twin_ysize = -SCREEN_H * 0.60;

 cwin_xbeg  =  SCREEN_W * 0.50;
 cwin_ybeg  =  SCREEN_H * 0.30;
 cwin_xsize =  SCREEN_W * 0.45;
 cwin_ysize = -SCREEN_H * 0.25;

 // buffer  = page2;
 // screen = page1;

 set_palette(default_palette);
 for (i=0; i<256; i++)
      grapal[i] = default_palette[i];
 text_mode(-1);

 return 0;
}



void start_allegro(void)
{
 allegro_init();

 install_keyboard();
 install_timer();
 setup_graph();
 install_mouse();
 set_mouse_range(0, 0, SCREEN_W-1, SCREEN_H-1); //Default values
 position_mouse(SCREEN_W/2,SCREEN_H/2);

 data = load_datafile("fonts.dat");
 if (!data) {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      allegro_message("Error loading fonts\n");
      exit(1);
 }

 font_big = (FONT *)find_datafile_object(data, "fnt8x8")->dat;
 font_med = (FONT *)find_datafile_object(data, "fnt7x8")->dat;
 font_sml = (FONT *)find_datafile_object(data, "fnt5x6")->dat;
 if (strcmp(fontfile,"") != 0 )
 {
    FILE *fp;
    if ((fp=fopen(fontfile,"r")) == NULL)
    {
        printf("Fatal error: Did not find defined font file >%s<\n", fontfile);
        exit(1);
    }
    fclose(fp);
    PALETTE palette;
    addFont = load_font(fontfile, palette, NULL);
    if (!addFont)
    {
        allegro_message("Unable to load font %s.\n", fontfile);
        exit(1);
    }
    if (addFont && fontfile)
    {
        usedTextFont = addFont;
    }
    else
    {
        usedTextFont = font;
    }
 }
 else
 {
     usedTextFont = font;
 }

  if (strcmp(sfontfile,"") != 0 )
 {
    FILE *fp;
    if ((fp=fopen(sfontfile,"r")) == NULL)
    {
        printf("Fatal error: Did not find defined small font file >%s<\n", sfontfile);
        exit(1);
    }
    fclose(fp);
    PALETTE palette;
    addSFont = load_font(sfontfile, palette, NULL);
    if (!addSFont)
    {
        allegro_message("Unable to load small font %s.\n", sfontfile);
        exit(1);
    }
    if (addSFont && sfontfile)
    {
        usedSTextFont = addSFont;
    }
    else
    {
        usedSTextFont = font_sml;
    }
 }
 else
 {
     usedSTextFont = font_sml;
 }

  if (strcmp(mfontfile,"") != 0 )
 {
    FILE *fp;
    if ((fp=fopen(mfontfile,"r")) == NULL)
    {
        printf("Fatal error: Did not find defined medium font file >%s<\n", mfontfile);
        exit(1);
    }
    fclose(fp);
    PALETTE palette;
    addMFont = load_font(mfontfile, palette, NULL);
    if (!addMFont)
    {
        allegro_message("Unable to load medium font %s.\n", mfontfile);
        exit(1);
    }
    if (addMFont && mfontfile)
    {
        usedMTextFont = addMFont;
    }
    else
    {
        usedMTextFont = font_med;
    }
 }
 else
 {
     usedMTextFont = font_med;
 }

  if (strcmp(bfontfile,"") != 0 )
 {
    FILE *fp;
    if ((fp=fopen(bfontfile,"r")) == NULL)
    {
        printf("Fatal error: Did not find defined big font file >%s<\n", bfontfile);
        exit(1);
    }
    fclose(fp);
    PALETTE palette;
    addBFont = load_font(bfontfile, palette, NULL);
    if (!addBFont)
    {
        allegro_message("Unable to load big font %s.\n", bfontfile);
        exit(1);
    }
    if (addBFont && bfontfile)
    {
        usedBTextFont = addBFont;
    }
    else
    {
        usedBTextFont = font_big;
    }
 }
 else
 {
     usedBTextFont = font_big;
 }


}



void showall(void)
{
 int i,j;

 start_sec   = 0;
 end_sec     = nsections-1;
 start_trace = 0;
 end_trace   = ntraces-1;

 minh = maxh = trace_poly[0][0][0];
 for (i=0; i<nsections; i++) {
   for (j=0; j<ntraces; j++) {
           if (minh > trace_poly[i][j][0]) minh=trace_poly[i][j][0];
           if (maxh < trace_poly[i][j][0]) maxh=trace_poly[i][j][0];
   }
 }
 minh = (int) (minh/TICKSPERMETER) - 1;
 maxh = (int) (maxh/TICKSPERMETER) + 1;
}



void init_calc(void)
{
 int i,j;

 for (i=0; i<nsections; i++) {
   for (j=0; j<ntraces; j++) {
     if (radius[i] != 0) {
       radseclength[i][j]=(int)seclength[i] * ((radius[i] - tracepos[j])/radius[i] );
       // Replace trace lengths that become <= 0 due to turn radius
       if (radius[i]-tracepos[j] == 0) {
         radseclength[i][j] = seclength[i];
         //printf("*0*0* %d %d\n",i,j);
       }
       if (radseclength[i][j] <= 0 ) {
         radseclength[i][j] = seclength[i];
         //printf("*-*-* %d %d\n",i,j);
       }
     }
     else {
       radseclength[i][j]=seclength[i];
     }
     if (i > 0) radsecbeg[i][j]=radsecbeg[i-1][j]+radseclength[i-1][j];
   }
 }

 seclength[nsections]=seclength[0];
 secbeg[nsections]=secbeg[0];
 for (j=0; j<ntraces; j++) {
   radseclength[nsections][j]=radseclength[0][j];
   radsecbeg[nsections][j]=radsecbeg[0][j];
   trace_poly[nsections][j][3]=trace_poly[0][j][3];
   trace_poly[nsections][j][2]=trace_poly[0][j][2];
   trace_poly[nsections][j][1]=trace_poly[0][j][1];
   trace_poly[nsections][j][0]=trace_poly[0][j][0];
 }

 for (i=0; i<nsections; i++) {
   secbegmeter[i] = secbeg[i] / TICKSPERMETER;
   for (j=0; j<ntraces; j++) {
     radsecbegmeter[i][j] = radsecbeg[i][j] / TICKSPERMETER;
   }
 }

 showall();
 cross_sec = 0;

 centreline_id = (ntraces-1)/2;

 if (centreline_override >= 0) centreline_id = centreline_override;

 trace_id = centreline_id;
 secdivs=16;

}



void init(void)
{
 intro();

 set_defaults();

 load_config();

 load_traces();

 if (scan_trk()) {
   load_tracepos();
   load_radius();
 }

 load_altref();

 init_calc();

 start_allegro();
}



void discofix(bool fixit)
{
 int okgap=1;       //0.05 mm height gap is accepted
 float okgrad=1e-4; //0.006 degree kink is accepted
 int numgapsect=0;
 int numgradsect=0;
 int hdif[MAXNTRACES];
 float grdif[MAXNTRACES];
 int i,j,k;

 scare_mouse();

 for (i=0; i<nsections; i++) {
   bool sectgap = false;
   bool gradbrk = false;
   for (j=0; j<ntraces; j++) {
     int h1, h2;
     float gr1, gr2;

     h1 = trace_poly[i][j][0]+trace_poly[i][j][1]+trace_poly[i][j][2]+trace_poly[i][j][3];
     h2 = trace_poly[i+1][j][0];
     hdif[j] = h1-h2;
     if (abs(h1 - h2) > okgap) sectgap = true;
     gr1 = (3 * trace_poly[i][j][3] + 2 * trace_poly[i][j][2] + trace_poly[i][j][1]) / (float) radseclength[i][j];
     gr2 = trace_poly[i+1][j][1] / (float) radseclength[i+1][j];
     grdif[j] = gr1-gr2;
     if (fabs(gr1 - gr2) > okgrad) gradbrk = true;

     if (fixit && ( (abs(h1 - h2) > okgap) || (fabs(gr1 - gr2) > okgrad) ) ) {
       int y, a, b, c, d, h0, dh, l, after;
       float alpha0, alpha1;

       changed = true;
       y = (h1 + h2)/2;
       // first section
       alpha1 = (gr1 + gr2)/2;
       d=trace_poly[i][j][0];
       c=trace_poly[i][j][1];
       h0=d;
       h1=y;
       dh=h1-h0;
       l=radseclength[i][j];
       a=alpha1*l-2*dh+c;
       b=-alpha1*l+3*dh-2*c;
       trace_poly[i][j][3] = a;
       trace_poly[i][j][2] = b;
       trace_poly[i][j][1] = c;
       trace_poly[i][j][0] = d;
       if (i == 0) {
         for (k=0; k<4; k++)
           trace_poly[nsections][j][k]=trace_poly[0][j][k];
       }

       //last section:
       if (i < nsections-1) after=i+2; else after=1;
       d=y;
       alpha0= (gr1 + gr2)/2;
       l=radseclength[i+1][j];
       c=alpha0*l;
       h0=d;
       h1=trace_poly[after][j][0];
       dh=h1-h0;
       alpha1=trace_poly[after][j][1]/(float) radseclength[after][j];
       a=alpha1*l-2*dh+c;
       b=-alpha1*l+3*dh-2*c;
       trace_poly[i+1][j][3] = a;
       trace_poly[i+1][j][2] = b;
       trace_poly[i+1][j][1] = c;
       trace_poly[i+1][j][0] = d;
       if (i+1 == nsections) {
         for (k=0; k<4; k++)
           trace_poly[0][j][k]=trace_poly[nsections][j][k];
       }
     }

    }

    if (sectgap) {
       textprintf(screen, usedTextFont, 4, 30+10*numgapsect, WHITE, "Section gap: %d   ", i);
      numgapsect++;
    }
    if (gradbrk) {
       textprintf(screen, usedTextFont, 150, 30+10*numgradsect, WHITE, "Section kink: %d", i);
      numgradsect++;
    }
  }
  if (numgapsect == 0)
    textprintf(screen, usedTextFont, 4, 10, WHITE, "No height gaps found.");
  else
    textprintf(screen, usedTextFont, 4, 10, WHITE, "%d sections had gaps.", numgapsect);

  if (numgradsect == 0)
    textprintf(screen, usedTextFont, 4, 20, WHITE, "No gradient breaks found.");
  else
    textprintf(screen, usedTextFont, 4, 20, WHITE, "%d sections had gradient breaks.", numgradsect);

  unscare_mouse();

  rest(500);
  if ( (numgapsect) || (numgradsect) ) rest(1000);
}



void keyinput(void) // Keyboard input. Only to be called if buffer is not empty
{
 int ind;

 ind=readkey();

 switch(ind >> 8) {

 case KEY_Q :
  if (key_shifts & KB_SHIFT_FLAG)
    quit=true;
  else {
    if (changed) {
            textprintf(screen, usedTextFont, 4, 10, WHITE, "Data are not saved!");
            textprintf(screen, usedTextFont, 4, 20, WHITE, "Press shift Q to quit");
            textprintf(screen, usedTextFont, 4, 30, WHITE, "Press w to save");
	    rest(1000);
    }
    else quit = true;
  }
 break;

 case KEY_W :
  save_traces();
 break;

 case KEY_DOWN :
  if (trace_id > 0) trace_id--;
 break;

 case KEY_UP :
  if (trace_id < ntraces-1) trace_id++;
 break;

 case KEY_LEFT :
  if (cross_sec > start_sec) cross_sec--;
 break;

 case KEY_RIGHT :
  if (cross_sec < end_sec) cross_sec++;
 break;

 case KEY_HOME :
  trace_id = centreline_id;
 break;

 case KEY_7_PAD :
  if (mouse_y > cwin_ybeg) {
    if (start_sec < end_sec-1) start_sec++;
    if (cross_sec < start_sec) cross_sec=start_sec;
  }
  else {
    if (start_trace < end_trace-1) start_trace++;
  }
 break;

 case KEY_1_PAD :
  if (mouse_y > cwin_ybeg) {
    if (start_sec > 0) start_sec--;
  }
  else {
    if (start_trace > 0) start_trace--;
  }
 break;

 case KEY_9_PAD :
  if (mouse_y > cwin_ybeg) {
    if (end_sec < nsections-1) end_sec++;
  }
  else {
    if (end_trace < ntraces-1) end_trace++;
  }
 break;

 case KEY_3_PAD :
  if (mouse_y > cwin_ybeg) {
    if (end_sec > start_sec+1) end_sec--;
    if (cross_sec > end_sec) cross_sec=end_sec;
  }
  else {
    if (end_trace > start_trace+1) end_trace--;
  }
 break;

 case KEY_INSERT :
  maxh = 1.05 * (maxh-minh) + minh;
 break;

 case KEY_DEL :
  maxh = 0.95 * (maxh-minh) + minh;
 break;

 case KEY_PGUP :
  minh = maxh - 0.95 * (maxh-minh);
 break;

 case KEY_PGDN :
  minh = maxh - 1.05 * (maxh-minh);
 break;

 case KEY_C :
  discofix(false);
 break;

 case KEY_F :
  discofix(true);
 break;

 case KEY_A :
  show_angles = !show_angles;
 break;

 case KEY_Z :
  showall();
 break;

 case KEY_H :
    help();
 break;

 default : ;

 }
}



int x2tw(float x)
{
 float fx = (x - tw_dat_xmin) / (tw_dat_xmax - tw_dat_xmin);
 return(twin_xbeg + (fx * twin_xsize));
}

int y2tw(float y)
{
 float fy = (y - tw_dat_ymin) / (tw_dat_ymax - tw_dat_ymin);
 return(twin_ybeg + (fy * twin_ysize));
}

void tw_line(BITMAP *bmp, float x1, float y1, float x2, float y2, int col)
{
 line(bmp, x2tw(x1), y2tw(y1), x2tw(x2), y2tw(y2), col);
}

float tw2x(int x)
{
 float fx = ( (float) x - twin_xbeg) / twin_xsize;
 fx = fx * (tw_dat_xmax - tw_dat_xmin) + tw_dat_xmin;
 return(fx);
}



int x2cw(float x)
{
 float fx = (x - cw_dat_xmin) / (cw_dat_xmax - cw_dat_xmin);
 return(cwin_xbeg + (fx * cwin_xsize));
}

int y2cw(float y)
{
 float fy = (y - cw_dat_ymin) / (cw_dat_ymax - cw_dat_ymin);
 return(cwin_ybeg + (fy * cwin_ysize));
}

void cw_line(float x1, float y1, float x2, float y2, int col)
{
 line(buffer, x2cw(x1), y2cw(y1), x2cw(x2), y2cw(y2), col);
}

float tw2y(int y)
{
 float fy = ( (float) y - twin_ybeg) / twin_ysize;
 fy = fy * (tw_dat_ymax - tw_dat_ymin) + tw_dat_ymin;
 return(fy);
}



void calcticks(float range, float *bigtick, float *smalltick)
{
 if ( range > 10000) {
   *bigtick = 5000;
   *smalltick = 1000;
 }
 else if ( range > 5000) {
   *bigtick = 2000;
   *smalltick = 500;
 }
 else if ( range > 1000) {
   *bigtick = 500;
   *smalltick = 100;
 }
 else if ( range > 500) {
   *bigtick = 200;
   *smalltick = 50;
 }
 else if ( range > 100) {
   *bigtick = 50;
   *smalltick = 10;
 }
 else if ( range > 50) {
   *bigtick = 20;
   *smalltick = 5;
 }
 else if ( range > 10) {
   *bigtick = 5;
   *smalltick = 1;
 }
 else if ( range > 5) {
   *bigtick = 2;
   *smalltick = 0.5;
 }
 else if ( range > 1) {
   *bigtick = 0.5;
   *smalltick = 0.1;
 }
 else if ( range > 0.5) {
   *bigtick = 0.2;
   *smalltick = 0.05;
 }
 else {
   *bigtick = 0.05;
   *smalltick = 0.01;
 }
}



void show_graph(void)
{
 int i,j,k;
 int nshow=end_sec-start_sec+1;
 int *tickidx;
 float *tickheight;
 float *meteridx;
 float *meterheight;
 float cross_height[MAXNTRACES];
 float cross_hmin, cross_hmax, cross_dh;
 int col;
 int xwid = text_length( usedSTextFont, " ");
 float bigtick, smalltick, tick;

 clear_bitmap(buffer);

 tickidx = malloc(nshow*secdivs*sizeof(int));
 tickheight = malloc(nshow*secdivs*ntraces*sizeof(float));
 meteridx = malloc(nshow*secdivs*sizeof(float));
 meterheight = malloc(nshow*secdivs*ntraces*sizeof(float));


 for (k=0; k<ntraces; k++) {
   for (i=0; i<nshow; i++) {
     for (j=0; j<secdivs; j++) {
      int idx = i*secdivs+j;
      float t = (float) j / secdivs;
      tickidx[idx] = radsecbeg[start_sec+i][trace_id]+t*radseclength[start_sec+i][trace_id];
      meteridx[idx] = tickidx[idx] / TICKSPERMETER;
      tickheight[idx+k*nshow*secdivs]=
        t*t*t*trace_poly[start_sec+i][k][3]+
        t*t*  trace_poly[start_sec+i][k][2]+
        t*    trace_poly[start_sec+i][k][1]+
              trace_poly[start_sec+i][k][0];
      meterheight[idx+k*nshow*secdivs]=tickheight[idx+k*nshow*secdivs] / TICKSPERMETER;

     }
   }
 }

 for (i=0; i<ntraces; i++) {
  cross_height[i] = trace_poly[cross_sec][i][0] / TICKSPERMETER;
 }

 cross_hmin = cross_hmax = cross_height[start_trace];
 for (i=start_trace; i<=end_trace; i++) {
   if (cross_hmin >  cross_height[i]) cross_hmin = cross_height[i];
   if (cross_hmax <  cross_height[i]) cross_hmax = cross_height[i];
 }
 cross_dh=0.1*(cross_hmax-cross_hmin)+0.1;
 cross_hmin=cross_hmin-cross_dh;
 cross_hmax=cross_hmax+cross_dh;

 cw_dat_xmin = tracepos[start_trace]-1;
 cw_dat_xmax = tracepos[end_trace]+1;
 cw_dat_ymin = cross_hmin;
 cw_dat_ymax = cross_hmax;

 scare_mouse();

 line(buffer,cwin_xbeg,cwin_ybeg,cwin_xbeg+cwin_xsize,cwin_ybeg,LIGHTGRAY);
 line(buffer,cwin_xbeg,cwin_ybeg,cwin_xbeg,cwin_ybeg+cwin_ysize,LIGHTGRAY);
 line(buffer,cwin_xbeg,cwin_ybeg+cwin_ysize,cwin_xbeg+cwin_xsize,cwin_ybeg+cwin_ysize,LIGHTGRAY);
 line(buffer,cwin_xbeg+cwin_xsize,cwin_ybeg,cwin_xbeg+cwin_xsize,cwin_ybeg+cwin_ysize,LIGHTGRAY);

 //Length ticks
 calcticks( cw_dat_xmax - cw_dat_xmin, &bigtick, &smalltick);

 for (tick = bigtick*(int)(cw_dat_xmin/bigtick) - bigtick; tick < cw_dat_xmax; tick=tick+bigtick) {
  if ( (tick >= cw_dat_xmin) && (tick <= cw_dat_xmax) ) {
     line(buffer, x2cw(tick), cwin_ybeg-3, x2cw(tick), cwin_ybeg+3, LIGHTGRAY);
     textprintf(buffer, usedSTextFont,
     x2cw(tick)-3*text_length(usedSTextFont," "),
     cwin_ybeg+text_height(usedSTextFont), WHITE,
     "%4.0f",tick);
  }
 }
 for (tick = smalltick*(int)(cw_dat_xmin/smalltick)-smalltick; tick < cw_dat_xmax; tick=tick+smalltick) {
  if ( (tick >= cw_dat_xmin) && (tick <= cw_dat_xmax) ) {
   line(buffer, x2cw(tick), cwin_ybeg-1, x2cw(tick), cwin_ybeg+1, LIGHTGRAY);
  }
 }

 set_clip(buffer, cwin_xbeg,cwin_ybeg,cwin_xbeg+cwin_xsize,cwin_ybeg+cwin_ysize);

 for (i=start_trace; i<end_trace; i++)
  cw_line(tracepos[i], cross_height[i], tracepos[i+1], cross_height[i+1], RED);
 for (i=start_trace; i<=end_trace; i++) {
  circlefill(buffer, x2cw(tracepos[i]), y2cw(cross_height[i]), 2, RED);
  textprintf(buffer, usedSTextFont, x2cw(tracepos[i])-xwid,
  y2cw(cross_hmax-0.05*(cross_hmax-cross_hmin)), LIGHTGRAY,
  "%2d",i);
 }
 if (trace_id == centreline_id) col = YELLOW; else col = WHITE;
 circlefill(buffer, x2cw(tracepos[trace_id]), y2cw(cross_height[trace_id]), 2, col);

 if (show_angles) {
  for (i=start_trace; i<end_trace; i++) {
   float dy = cross_height[i+1]-cross_height[i];
   float dx = tracepos[i+1]-tracepos[i];
   if (dx != 0) {
    float bank=atan(dy/dx)*180/PI;
    textprintf(buffer, usedSTextFont,
    x2cw((tracepos[i]+tracepos[i+1])/2)-4*xwid,
    y2cw(cross_hmax-0.10*(cross_hmax-cross_hmin)), WHITE,
    "%6.1f",bank);
   }
  }
 }

 set_clip(buffer, 0, 0, SCREEN_W, SCREEN_H);

 tw_dat_xmin = meteridx[0];
 tw_dat_xmax = meteridx[nshow*secdivs-1];
 tw_dat_ymin = minh;
 tw_dat_ymax = maxh;

 // Frame
 line(buffer,twin_xbeg-1,twin_ybeg+1,twin_xbeg+twin_xsize,twin_ybeg+1,LIGHTGRAY);
 line(buffer,twin_xbeg-1,twin_ybeg,twin_xbeg-1,twin_ybeg+twin_ysize,LIGHTGRAY);
 line(buffer,twin_xbeg-1,twin_ybeg+twin_ysize-1,twin_xbeg+twin_xsize,twin_ybeg+twin_ysize-1,LIGHTGRAY);
 line(buffer,twin_xbeg+twin_xsize,twin_ybeg,twin_xbeg+twin_xsize,twin_ybeg+twin_ysize,LIGHTGRAY);

 //Height ticks
 calcticks( maxh - minh, &bigtick, &smalltick);

 for (tick = bigtick*(int)(minh/bigtick) - bigtick; tick < maxh; tick=tick+bigtick) {
  if ( (tick >= minh) && (tick <= maxh) ) {
     line(buffer, twin_xbeg-5, y2tw(tick), twin_xbeg+5, y2tw(tick), LIGHTGRAY);
   if (bigtick > 1)
     textprintf(buffer, usedMTextFont,
     twin_xbeg-5*text_length(usedMTextFont," "),
     y2tw(tick)-0.5*text_height(usedMTextFont), WHITE,
     "%4.0f",tick);
   else
     textprintf(buffer, usedMTextFont,
     twin_xbeg-6*text_length(usedMTextFont," "),
     y2tw(tick)-0.5*text_height(usedMTextFont), WHITE,
     "%5.2f",tick);
     }
 }
 for (tick = smalltick*(int)(minh/smalltick)-smalltick; tick < maxh; tick=tick+smalltick) {
  if ( (tick >= minh) && (tick <= maxh) ) {
   line(buffer, twin_xbeg-2, y2tw(tick), twin_xbeg+2, y2tw(tick), LIGHTGRAY);
  }
 }

 //Length ticks
 calcticks( meteridx[nshow*secdivs-1] - meteridx[0], &bigtick, &smalltick);

 for (tick = bigtick*(int)(meteridx[0]/bigtick) - bigtick; tick < meteridx[nshow*secdivs-1]; tick=tick+bigtick) {
  if ( (tick >= meteridx[0]) && (tick <= meteridx[nshow*secdivs-1]) ) {
     line(buffer, x2tw(tick), twin_ybeg-5, x2tw(tick), twin_ybeg+5, LIGHTGRAY);
     textprintf(buffer, usedMTextFont,
     x2tw(tick)-3*text_length(usedMTextFont," "),
     twin_ybeg+1.5*text_height(usedMTextFont), WHITE,
     "%5.0f",tick);
  }
 }
 for (tick = smalltick*(int)(meteridx[0]/smalltick)-smalltick; tick < meteridx[nshow*secdivs-1]; tick=tick+smalltick) {
  if ( (tick >= meteridx[0]) && (tick <= meteridx[nshow*secdivs-1]) ) {
   line(buffer, x2tw(tick), twin_ybeg-2, x2tw(tick), twin_ybeg+2, LIGHTGRAY);
  }
 }

 set_clip(buffer, twin_xbeg,twin_ybeg,twin_xbeg+twin_xsize,twin_ybeg+twin_ysize);

 for (i=0; i<nshow; i++) {
  float lx=radsecbeg[start_sec+i][trace_id]/TICKSPERMETER;
  float lcx;

  col = BLUE;

  if (start_sec+i == cross_sec) col = LIGHTCYAN;
  tw_line(buffer, lx, minh, lx, maxh, col);

  lcx=(radsecbeg[start_sec+i][trace_id] + radseclength[start_sec+i][trace_id]/2)/TICKSPERMETER;
  textprintf(buffer, usedSTextFont, x2tw(lcx)-2*xwid,
  y2tw(maxh-0.05*(maxh-minh)), LIGHTGRAY,
  "%3d", start_sec+i);

  if (show_angles) {
   float dydx= trace_poly[start_sec+i][trace_id][1] / (float)radseclength[start_sec+i][trace_id];
   float climb=atan(dydx)*180/PI;
   textprintf(buffer, usedSTextFont,
   x2tw(lx)-4*xwid, y2tw(maxh-0.10*(maxh-minh)), WHITE,
   "%6.1f",climb);
  }

 }

 for (i=0; i<ntraces; i++) {
  for (j=0; j<nshow*secdivs-1; j++) {
    tw_line(buffer, meteridx[j], meterheight[j+i*nshow*secdivs], meteridx[j+1], meterheight[j+1+i*nshow*secdivs], RED);
  }
 }

 if (trace_id == centreline_id) col = YELLOW; else col = WHITE;
 for (j=0; j<nshow*secdivs-1; j++) {
   tw_line(buffer, meteridx[j], meterheight[j+trace_id*nshow*secdivs], meteridx[j+1], meterheight[j+1+trace_id*nshow*secdivs], col);
 }

 if (using_altref && (trace_id == centreline_id)) {
  for (i=0; i<naltref; i++) {
   int cx = x2tw(refpos[i]);
   int cy = y2tw(refalt[i]);
   line(buffer,cx-2,cy,cx+2,cy,WHITE);
   line(buffer,cx,cy-2,cx,cy+2,WHITE);
  }
 }

 set_clip(buffer, 0, 0, SCREEN_W, SCREEN_H);

 unscare_mouse();

 update_screen();
 // flip();

 free(tickidx);
 free(tickheight);
 free(meteridx);
 free(meterheight);
}



void show_sect(int sec, float tracepol[])
{
 int j;
 int *tickidx;
 float *tickheight;
 float *meteridx;
 float *meterheight;

 tickidx = malloc(secdivs*sizeof(int));
 tickheight = malloc(secdivs*sizeof(float));
 meteridx = malloc(secdivs*sizeof(float));
 meterheight = malloc(secdivs*sizeof(float));

 for (j=0; j<secdivs; j++) {
      float t = (float) j / (secdivs-1);
      tickidx[j] = radsecbeg[sec][trace_id]+t*radseclength[sec][trace_id];
      meteridx[j] = tickidx[j] / TICKSPERMETER;
      tickheight[j]=
        t*t*t*tracepol[3]+
        t*t*  tracepol[2]+
        t*    tracepol[1]+
              tracepol[0];
      meterheight[j]=tickheight[j] / TICKSPERMETER;

     }

 for (j=0; j<secdivs-1; j++) {
    tw_line(screen, meteridx[j], meterheight[j], meteridx[j+1], meterheight[j+1], LIGHTCYAN);
  }

 free(tickidx);
 free(tickheight);
 free(meteridx);
 free(meterheight);
}



int nearest_section(float x)
{
 //Find closest section beginning
 float secdist = 1e6;
 int i;
 int secid = 0;

 for (i=0; i<nsections; i++) {
  float thisdist = fabs(x - radsecbegmeter[i][trace_id]);

  if (thisdist < secdist) {
   secdist = thisdist;
   secid = i;
  }
 }
 return secid;
}



void show_edit_sec(float dgx, float dgy, float y, int sec)
{
 int a,b,c,d;
 float h0, h1, dh, l;
 float alpha0, alpha1;
 float tmp_trace_poly[4];

 if (sec == 0) sec = nsections;

 d=trace_poly[sec-1][trace_id][0];
 c=trace_poly[sec-1][trace_id][1];
 alpha1=dgy/(float)dgx;
 h0=d;
 h1=y*TICKSPERMETER;
 dh=h1-h0;
 l=radseclength[sec-1][trace_id];
 a=alpha1*l-2*dh+c;
 b=-alpha1*l+3*dh-2*c;
 tmp_trace_poly[3] = a;
 tmp_trace_poly[2] = b;
 tmp_trace_poly[1] = c;
 tmp_trace_poly[0] = d;
 show_sect(sec-1, tmp_trace_poly);

 if (sec == nsections) sec = 0;

 d=y*TICKSPERMETER;
 alpha0=dgy/(float)dgx;
 l=radseclength[sec][trace_id];
 c=alpha0*l;
 h0=d;
 h1=trace_poly[sec+1][trace_id][0];
 dh=h1-h0;
 alpha1=trace_poly[sec+1][trace_id][1]/(float)radseclength[sec+1][trace_id];
 a=alpha1*l-2*dh+c;
 b=-alpha1*l+3*dh-2*c;
 tmp_trace_poly[3] = a;
 tmp_trace_poly[2] = b;
 tmp_trace_poly[1] = c;
 tmp_trace_poly[0] = d;
 show_sect(sec, tmp_trace_poly);
}


int mouse1input(void)
{
 float x = tw2x(mouse_x);
 float y = tw2y(mouse_y);
 int secid = 0;
 int i;
 float gx, gy, dgx, dgy;
 int a,b,c,d;
 float h0, h1, dh, l;
 float alpha0, alpha1;
 int x1, y1, x2, y2;

 mbtn1_press = true;

 if ( (mouse_x < twin_xbeg) || (mouse_x > twin_xbeg+twin_xsize) ||
      (mouse_y > twin_ybeg) || (mouse_y < twin_ybeg+twin_ysize) )
      return(0);

 secid = nearest_section(x);

 if (secid != cross_sec) {
  cross_sec = secid;
  show_graph();
  return(0);
 }

 x1 = mouse_x;
 y1 = mouse_y;
 x  = tw2x(mouse_x);
 y  = tw2y(mouse_y);

 scare_mouse();
 textprintf(screen, usedTextFont, 4, 10, WHITE, "Release MBTN1 to set new height.");
 drawing_mode(DRAW_MODE_XOR, screen, 0, 0);
 set_clip(screen, twin_xbeg,twin_ybeg,twin_xbeg+twin_xsize,twin_ybeg+twin_ysize);
 show_edit_sec(radseclength[secid][trace_id], trace_poly[secid][trace_id][1], y, secid);
 unscare_mouse();

 while (mouse_b) {

  if ( (x1 != mouse_x) || ( y1 != mouse_y) ) {
   scare_mouse();
 show_edit_sec(radseclength[secid][trace_id], trace_poly[secid][trace_id][1], y, secid);
   unscare_mouse();
   x1 = mouse_x;
   y1 = mouse_y;
   x  = tw2x(mouse_x);
   y  = tw2y(mouse_y);
   scare_mouse();
 show_edit_sec(radseclength[secid][trace_id], trace_poly[secid][trace_id][1], y, secid);

   set_clip(screen, 0, 0, SCREEN_W, SCREEN_H);
   drawing_mode(DRAW_MODE_SOLID, screen, 0, 0);
   rectfill(screen,  4+text_length(usedMTextFont, "Current height:"), 60,
   4+text_length(usedMTextFont, "Current height:       "), 60+text_height(usedMTextFont), BLACK);
   textprintf(screen, usedMTextFont, 4, 60, WHITE,
   "Current height: %6.2f m",y);
   drawing_mode(DRAW_MODE_XOR, screen, 0, 0);
   set_clip(screen, twin_xbeg,twin_ybeg,twin_xbeg+twin_xsize,twin_ybeg+twin_ysize);


   unscare_mouse();
  }
 }
 scare_mouse();
 show_edit_sec(radseclength[secid][trace_id], trace_poly[secid][trace_id][1], y, secid);
 unscare_mouse();
 drawing_mode(DRAW_MODE_SOLID, screen, 0, 0);
 set_clip(screen, 0, 0, SCREEN_W, SCREEN_H);

 show_graph();

 scare_mouse();
 circle(screen, x2tw(radsecbegmeter[secid][trace_id]), y2tw(y), 2, WHITE);
 textprintf(screen, usedTextFont, 4, 10, WHITE, "New height set!");
 textprintf(screen, usedTextFont, 4, 20, WHITE, "Now define gradient");
 textprintf(screen, usedTextFont, 4, 30, WHITE, "MBTN1 to set");
 textprintf(screen, usedTextFont, 4, 40, WHITE, "MBTN2 to cancel");
 unscare_mouse();

 x2 = mouse_x;
 y2 = mouse_y;
 gx  = tw2x(mouse_x);
 gy  = tw2y(mouse_y);
 dgx = gx - radsecbegmeter[secid][trace_id];
 dgy = gy - y;

 drawing_mode(DRAW_MODE_XOR, screen, 0, 0);
 set_clip(screen, twin_xbeg,twin_ybeg,twin_xbeg+twin_xsize,twin_ybeg+twin_ysize);
 scare_mouse();
 line(screen, x2tw(radsecbegmeter[secid][trace_id]-dgx), y2tw(y-dgy),
                    x2tw(radsecbegmeter[secid][trace_id]+dgx), y2tw(y+dgy), WHITE);
 show_edit_sec(dgx, dgy, y, secid);
 unscare_mouse();

 while (!mouse_b) {
  if ( (x2 != mouse_x) || ( y2 != mouse_y) ) {
   scare_mouse();
   line(screen, x2tw(radsecbegmeter[secid][trace_id]-dgx), y2tw(y-dgy),
                    x2tw(radsecbegmeter[secid][trace_id]+dgx), y2tw(y+dgy), WHITE);
   show_edit_sec(dgx, dgy, y, secid);

   unscare_mouse();
   x2 = mouse_x;
   y2 = mouse_y;
   gx  = tw2x(mouse_x);
   gy  = tw2y(mouse_y);
   dgx = gx - radsecbegmeter[secid][trace_id];
   dgy = gy - y;
   scare_mouse();
   line(screen, x2tw(radsecbegmeter[secid][trace_id]-dgx), y2tw(y-dgy),
                    x2tw(radsecbegmeter[secid][trace_id]+dgx), y2tw(y+dgy), WHITE);
   show_edit_sec(dgx, dgy, y, secid);

   if (show_angles) {
    //float dydx= trace_poly[secid][trace_id][1] / (float)radseclength[secid][trace_id];
   float dydx= dgy/dgx;
   float climb=atan(dydx)*180/PI;

   set_clip(screen, 0, 0, SCREEN_W, SCREEN_H);
   drawing_mode(DRAW_MODE_SOLID, screen, 0, 0);
   rectfill(screen,  4+text_length(usedMTextFont, "Current gradient: "), 60,
   4+text_length(usedMTextFont, "Current gradient:       "), 60+text_height(usedMTextFont), BLACK);
   textprintf(screen, usedMTextFont, 4, 60, WHITE,
   "Current gradient: %6.1f degrees",climb);
   drawing_mode(DRAW_MODE_XOR, screen, 0, 0);
   set_clip(screen, twin_xbeg,twin_ybeg,twin_xbeg+twin_xsize,twin_ybeg+twin_ysize);
   }
   unscare_mouse();
  }
 }
 drawing_mode(DRAW_MODE_SOLID, screen, 0, 0);
 set_clip(screen, 0, 0, SCREEN_W, SCREEN_H);

 if (mouse_b & 2) {
  while(mouse_b);
  show_graph();
  return(0);
 }

 if (secid == 0) secid = nsections;

 //First section:
 d=trace_poly[secid-1][trace_id][0];
 c=trace_poly[secid-1][trace_id][1];
 alpha1=dgy/(float)dgx;
 h0=d;
 h1=y*TICKSPERMETER;
 dh=h1-h0;
 l=radseclength[secid-1][trace_id];
 a=alpha1*l-2*dh+c;
 b=-alpha1*l+3*dh-2*c;

 if (trace_id == centreline_id) {
  int delta_a, delta_b, delta_c, delta_d;

  //If changes are made to the centreline trace, add change to all traces
  delta_a = a - trace_poly[secid-1][trace_id][3];
  delta_b = b - trace_poly[secid-1][trace_id][2];
  delta_c = c - trace_poly[secid-1][trace_id][1];
  delta_d = d - trace_poly[secid-1][trace_id][0];

  for (i=0; i<ntraces; i++) {
    trace_poly[secid-1][i][3] = trace_poly[secid-1][i][3] + delta_a;
    trace_poly[secid-1][i][2] = trace_poly[secid-1][i][2] + delta_b;
    trace_poly[secid-1][i][1] = trace_poly[secid-1][i][1] + delta_c;
    trace_poly[secid-1][i][0] = trace_poly[secid-1][i][0] + delta_d;
   }
 }

 //Perform change on current trace
 trace_poly[secid-1][trace_id][3] = a;
 trace_poly[secid-1][trace_id][2] = b;
 trace_poly[secid-1][trace_id][1] = c;
 trace_poly[secid-1][trace_id][0] = d;

 //last section:
 if (secid == nsections) secid = 0;
 d=y*TICKSPERMETER;
 alpha0=dgy/(float)dgx;
 l=radseclength[secid][trace_id];
 c=alpha0*l;
 h0=d;
 h1=trace_poly[secid+1][trace_id][0];
 dh=h1-h0;
 alpha1=trace_poly[secid+1][trace_id][1]/(float)radseclength[secid+1][trace_id];
 a=alpha1*l-2*dh+c;
 b=-alpha1*l+3*dh-2*c;

 if (trace_id == centreline_id) {
  int delta_a, delta_b, delta_c, delta_d;

  //If changes are made to the centreline trace, add change to all traces
  delta_a = a - trace_poly[secid][trace_id][3];
  delta_b = b - trace_poly[secid][trace_id][2];
  delta_c = c - trace_poly[secid][trace_id][1];
  delta_d = d - trace_poly[secid][trace_id][0];

  for (i=0; i<ntraces; i++) {
    trace_poly[secid][i][3] = trace_poly[secid][i][3] + delta_a;
    trace_poly[secid][i][2] = trace_poly[secid][i][2] + delta_b;
    trace_poly[secid][i][1] = trace_poly[secid][i][1] + delta_c;
    trace_poly[secid][i][0] = trace_poly[secid][i][0] + delta_d;
   }
 }

 //Perform change on current trace
 trace_poly[secid][trace_id][3] = a;
 trace_poly[secid][trace_id][2] = b;
 trace_poly[secid][trace_id][1] = c;
 trace_poly[secid][trace_id][0] = d;

 for (i=0; i<ntraces; i++) {
   trace_poly[nsections][i][3]=trace_poly[0][i][3];
   trace_poly[nsections][i][2]=trace_poly[0][i][2];
   trace_poly[nsections][i][1]=trace_poly[0][i][1];
   trace_poly[nsections][i][0]=trace_poly[0][i][0];
 }

 show_graph();

 changed = true;

 return(0);
}



int mouse2zoom(void)
{
 int x1 = mouse_x;
 int y1 = mouse_y;
 int x2, y2, tmp;

 scare_mouse();
 textprintf(screen, usedTextFont, 4, 10, WHITE, "First zoom corner set!");
 textprintf(screen, usedTextFont, 4, 20, WHITE, "Now release MBTN2 at other corner");
 unscare_mouse();

 x2 = mouse_x;
 y2 = mouse_y;

 drawing_mode(DRAW_MODE_XOR, screen, 0, 0);
 while (mouse_b) {
  if ( (x2 != mouse_x) || ( y2 != mouse_y) ) {
    scare_mouse();
    rect(screen, x1, y1, x2, y2, WHITE);
    unscare_mouse();
    x2 = mouse_x;
    y2 = mouse_y;
    scare_mouse();
    rect(screen, x1, y1, x2, y2, WHITE);
    unscare_mouse();
  }

 }

 drawing_mode(DRAW_MODE_SOLID, screen, 0, 0);
 x2 = mouse_x;
 y2 = mouse_y;

 if (x1 > x2) {
  tmp = x1;
  x1 = x2;
  x2 = tmp;
 }

 if (y1 > y2) {
  tmp = y1;
  y1 = y2;
  y2 = tmp;
 }

 if (nearest_section(tw2x(x1)) == nearest_section(tw2x(x2))) {
   textprintf(screen, usedTextFont, 4, 30, WHITE, "Section zoom range too small!");
   rest(500);
   show_graph();
   return(0);
 }

 start_sec = nearest_section(tw2x(x1));
 end_sec   = nearest_section(tw2x(x2));

 maxh = tw2y(y1);
 minh = tw2y(y2);

 if (cross_sec < start_sec) cross_sec = start_sec;
 if (cross_sec > end_sec  ) cross_sec = end_sec;

 show_graph();

 return(0);
}



int show_height(void)
{
   float y  = tw2y(mouse_y);

   if (mouse_y==showheight_last_y) return(0);
   else showheight_last_y = mouse_y;

   scare_mouse();
   drawing_mode(DRAW_MODE_SOLID, screen, 0, 0);
   rectfill(screen,  4+text_length(usedMTextFont, "Current height:"), 60,
   4+text_length(usedMTextFont, "Current height:       "), 60+text_height(usedMTextFont), BLACK);
   textprintf(screen, usedMTextFont, 4, 60, WHITE,
   "Current height: %6.2f m",y);
   unscare_mouse();

 return(0);
}



int main(int argc, char * argv[])
{
 init();

 show_graph();
 while (!quit) {
   if (keypressed()) {
     keyinput();
     show_graph();
     showheight_last_y = -1;
   }
   if (mouse_b & 1) {
     if (!mbtn1_press) mouse1input();
   }
   else mbtn1_press = false;
   if (mouse_b & 2) {
     mouse2zoom();
   }
   show_height();

 }

 return(0);
}

END_OF_MAIN()
