/*
 *  acm : an aerial combat simulator for X
 *  Copyright (C) 1991-1997  Riley Rainey
 *
 *  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; version 2 dated June, 1991.
 *
 *  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.
 */

/*
 *  Runway markings are designed to conform to FAA AC 150/5340-1G
 *  dated 9/27/93.
 *
 *  2006-04-24  Added runway numbers. Not sure if it complies to something,
 *  but it looks good :-) [U.S.]
 */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "../util/memory.h"
#include "../V/Vlibmath.h"
#include "../util/units.h"

#define runway_IMPORT
#include "runway.h"

#define CENTERLINE_WIDTH             units_FEETtoMETERS(3.0)
#define CENTERLINE_LENGTH            units_FEETtoMETERS(120.0)
#define CENTERLINE_GAP               units_FEETtoMETERS(80.0)
#define THRESHOLD_STRIPE_WIDTH       units_FEETtoMETERS(5.75)
#define THRESHOLD_STRIPE_LENGTH      units_FEETtoMETERS(150.0)
#define THRESHOLD_STRIPE_X_OFFSET    units_FEETtoMETERS(20.0)
#define THRESHOLD_STRIPE_Y_OFFSET    units_FEETtoMETERS(3.0)
#define THRESHOLD_STRIPE_MARGIN      units_FEETtoMETERS(5.75)
#define FIXED_MARKER_LENGTH          units_FEETtoMETERS(150.0)
#define FIXED_MARKER_WIDTH           units_FEETtoMETERS(30.0)
#define FIXED_MARKER_Y_OFFSET        units_FEETtoMETERS(3.0)
#define FIXED_MARKER_X_OFFSET        units_FEETtoMETERS(1000.0)
#define RUNWAY_COLOR                 "#222"
#define WHITE_PAINT                  "#ccc"
#define RUNWAY_CULL_DISTANCE         20000.0 /* m */
#define MARKING_CULL_DISTANCE        5000.0  /* m */


/*
	Utilities to draw the runway name
	---------------------------------

	Every char is defined inside a grid of 15x20 points, then scaled
	according to CHAR_SCALE_X and CHAR_SCALE_Y to buil a polygon.
*/

#define MAX_POINTS_PER_CHAR 20 /* max vertex per char polygon */
#define CHAR_SCALE_X 0.3  /* grid-to-meters width scale factor */
#define CHAR_SCALE_Y 1.0  /* grid-to-meters height scale factor */
#define CHAR_WIDTH (15*CHAR_SCALE_X)  /* total char width (meters) */
#define CHAR_HEIGHT (20*CHAR_SCALE_Y)  /* total char height (meters) */
#define CHAR_MARGIN_X 2.0 /* inter-char spacing (meters) */
#define CHAR_MARGIN_Y 6.0 /* inter-line spacing (meters) */
#define CHAR_X_OFFSET (THRESHOLD_STRIPE_X_OFFSET + THRESHOLD_STRIPE_LENGTH + 12.0) /* X offset of the chars from the threshold stripes (meters) */
#define CHAR_DEFAULT '/'

typedef struct {
	int np;
	int xy[2*MAX_POINTS_PER_CHAR];
} Character;

/**
 * The entry no. i is the ASCII char code i+32.
 */
static Character ** characters = NULL;

static VColor_Type *runwayColor, *whitePaintColor;


static void runway_cleanup()
{
	int i;

	if( characters == NULL )
		return;

	for( i=0; i<128-32; i++ )
		if( characters[i] != NULL )
			memory_dispose(characters[i]);

	memory_dispose(characters);
	characters = NULL;
}

/**
 * Fill-in the array of the chars 'characters'. It is called automatically
 * by BuildChar().
 * Actually only the digits and the letters L,C,R are defined, and all the
 * other chars are displayed as '/'.
 */
static void InitBuildChar()
{
	Character *s;
	int i;

	characters = memory_allocate((128-32)*sizeof(Character *), NULL);

	for ( i=0; i<128-32; i++ ) {
		characters[i] = NULL;
	}

	/* "/" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 4;
	s->xy[0] = 0;  s->xy[1] = 3;
	s->xy[2] = 3;  s->xy[3] = 0;
	s->xy[4] = 15;  s->xy[5] = 17;
	s->xy[6] = 12;  s->xy[7] = 20;
	characters['/'-32] = s;

	/* "0" and "O" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 13;
	s->xy[0] = 0;  s->xy[1] = 4;
	s->xy[2] = 3;  s->xy[3] = 0;
	s->xy[4] = 12;  s->xy[5] = 0;
	s->xy[6] = 15;  s->xy[7] = 4;
	s->xy[8] = 15;  s->xy[9] = 16;
	s->xy[10] = 12;  s->xy[11] = 20;
	s->xy[12] = 3;  s->xy[13] = 20;
	s->xy[14] = 0;  s->xy[15] = 16;
	s->xy[16] = 0;  s->xy[17] = 4;
	s->xy[18] = 4;  s->xy[19] = 4;
	s->xy[20] = 4;  s->xy[21] = 16;
	s->xy[22] = 11;  s->xy[23] = 16;
	s->xy[24] = 11;  s->xy[25] = 4;
	characters['0'-32] = s;
	characters['O'-32] = memcpy(memory_allocate(sizeof(Character), NULL), s, sizeof(Character));

	/* "1" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 6;
	s->xy[0] = 6;  s->xy[1] = 0;
	s->xy[2] = 9;  s->xy[3] = 0;
	s->xy[4] = 9;  s->xy[5] = 20;
	s->xy[6] = 6;  s->xy[7] = 20;
	s->xy[8] = 4;  s->xy[9] = 15;
	s->xy[10] = 6;  s->xy[11] = 15;
	characters['1'-32] = s;

	/* "2" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 16;
	s->xy[0] = 15;  s->xy[1] = 0;
	s->xy[2] = 15;  s->xy[3] = 3;
	s->xy[4] = 5;  s->xy[5] = 3;
	s->xy[6] = 15;  s->xy[7] = 11;
	s->xy[8] = 15;  s->xy[9] = 15;
	s->xy[10] = 10;  s->xy[11] = 20;
	s->xy[12] = 4;  s->xy[13] = 20;
	s->xy[14] = 0;  s->xy[15] = 15;
	s->xy[16] = 0;  s->xy[17] = 13;
	s->xy[18] = 3;  s->xy[19] = 13;
	s->xy[20] = 5;  s->xy[21] = 16;
	s->xy[22] = 9;  s->xy[23] = 16;
	s->xy[24] = 11;  s->xy[25] = 14;
	s->xy[26] = 11;  s->xy[27] = 11;
	s->xy[28] = 0;  s->xy[29] = 3;
	s->xy[30] = 0;  s->xy[31] = 0;
	characters['2'-32] = s;

	/* "3" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 18;
	s->xy[0] = 0;  s->xy[1] = 5;
	s->xy[2] = 0;  s->xy[3] = 2;
	s->xy[4] = 3;  s->xy[5] = 0;
	s->xy[6] = 12;  s->xy[7] = 0;
	s->xy[8] = 15;  s->xy[9] = 2;
	s->xy[10] = 15;  s->xy[11] = 8;
	s->xy[12] = 12;  s->xy[13] = 11;
	s->xy[14] = 15;  s->xy[15] = 14;
	s->xy[16] = 15;  s->xy[17] = 20;
	s->xy[18] = 0;  s->xy[19] = 20;
	s->xy[20] = 0;  s->xy[21] = 16;
	s->xy[22] = 11;  s->xy[23] = 16;
	s->xy[24] = 8;  s->xy[25] = 12;
	s->xy[26] = 8;  s->xy[27] = 9;
	s->xy[28] = 11;  s->xy[29] = 7;
	s->xy[30] = 11;  s->xy[31] = 3;
	s->xy[32] = 4;  s->xy[33] = 3;
	s->xy[34] = 4;  s->xy[35] = 5;
	characters['3'-32] = s;

	/* "4" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 14;
	s->xy[0] = 8;  s->xy[1] = 0;
	s->xy[2] = 12;  s->xy[3] = 0;
	s->xy[4] = 12;  s->xy[5] = 4;
	s->xy[6] = 15;  s->xy[7] = 4;
	s->xy[8] = 15;  s->xy[9] = 8;
	s->xy[10] = 12;  s->xy[11] = 8;
	s->xy[12] = 12;  s->xy[13] = 20;
	s->xy[14] = 8;  s->xy[15] = 20;
	s->xy[16] = 0;  s->xy[17] = 8;
	s->xy[18] = 0;  s->xy[19] = 4;
	s->xy[20] = 8;  s->xy[21] = 4;
	s->xy[22] = 8;  s->xy[23] = 8;
	s->xy[24] = 5;  s->xy[25] = 8;
	s->xy[26] = 8;  s->xy[27] = 12;
	characters['4'-32] = s;

	/* "5" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 14;
	s->xy[0] = 1;  s->xy[1] = 0;
	s->xy[2] = 12;  s->xy[3] = 0;
	s->xy[4] = 14;  s->xy[5] = 2;
	s->xy[6] = 14;  s->xy[7] = 10;
	s->xy[8] = 12;  s->xy[9] = 12;
	s->xy[10] = 5;  s->xy[11] = 12;
	s->xy[12] = 5;  s->xy[13] = 16;
	s->xy[14] = 14;  s->xy[15] = 16;
	s->xy[16] = 14;  s->xy[17] = 20;
	s->xy[18] = 1;  s->xy[19] = 20;
	s->xy[20] = 1;  s->xy[21] = 8;
	s->xy[22] = 10;  s->xy[23] = 8;
	s->xy[24] = 10;  s->xy[25] = 4;
	s->xy[26] = 1;  s->xy[27] = 4;
	characters['5'-32] = s;

	/* "6" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 15;
	s->xy[0] = 0;  s->xy[1] = 2;
	s->xy[2] = 3;  s->xy[3] = 0;
	s->xy[4] = 12;  s->xy[5] = 0;
	s->xy[6] = 15;  s->xy[7] = 2;
	s->xy[8] = 15;  s->xy[9] = 11;
	s->xy[10] = 12;  s->xy[11] = 13;
	s->xy[12] = 5;  s->xy[13] = 13;
	s->xy[14] = 4;  s->xy[15] = 9;
	s->xy[16] = 11;  s->xy[17] = 9;
	s->xy[18] = 11;  s->xy[19] = 4;
	s->xy[20] = 4;  s->xy[21] = 4;
	s->xy[22] = 4;  s->xy[23] = 9;
	s->xy[24] = 8;  s->xy[25] = 20;
	s->xy[26] = 4;  s->xy[27] = 20;
	s->xy[28] = 0;  s->xy[29] = 10;
	characters['6'-32] = s;

	/* "7" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 10;
	s->xy[0] = 4;  s->xy[1] = 0;
	s->xy[2] = 8;  s->xy[3] = 0;
	s->xy[4] = 8;  s->xy[5] = 4;
	s->xy[6] = 15;  s->xy[7] = 12;
	s->xy[8] = 15;  s->xy[9] = 20;
	s->xy[10] = 0;  s->xy[11] = 20;
	s->xy[12] = 0;  s->xy[13] = 16;
	s->xy[14] = 11;  s->xy[15] = 16;
	s->xy[16] = 11;  s->xy[17] = 13;
	s->xy[18] = 4;  s->xy[19] = 6;
	characters['7'-32] = s;

	/* "8" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 18;
	s->xy[0] = 2;  s->xy[1] = 0;
	s->xy[2] = 4;  s->xy[3] = 0;
	s->xy[4] = 4;  s->xy[5] = 16;
	s->xy[6] = 11;  s->xy[7] = 16;
	s->xy[8] = 11;  s->xy[9] = 12;
	s->xy[10] = 4;  s->xy[11] = 12;
	s->xy[12] = 4;  s->xy[13] = 8;
	s->xy[14] = 11;  s->xy[15] = 8;
	s->xy[16] = 11;  s->xy[17] = 4;
	s->xy[18] = 4;  s->xy[19] = 4;
	s->xy[20] = 4;  s->xy[21] = 0;
	s->xy[22] = 13;  s->xy[23] = 0;
	s->xy[24] = 15;  s->xy[25] = 2;
	s->xy[26] = 15;  s->xy[27] = 18;
	s->xy[28] = 13;  s->xy[29] = 20;
	s->xy[30] = 2;  s->xy[31] = 20;
	s->xy[32] = 0;  s->xy[33] = 18;
	s->xy[34] = 0;  s->xy[35] = 2;
	characters['8'-32] = s;

	/* "9" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 15;
	s->xy[0] = 15;  s->xy[1] = 18;
	s->xy[2] = 12;  s->xy[3] = 20;
	s->xy[4] = 3;  s->xy[5] = 20;
	s->xy[6] = 0;  s->xy[7] = 18;
	s->xy[8] = 0;  s->xy[9] = 9;
	s->xy[10] = 3;  s->xy[11] = 7;
	s->xy[12] = 10;  s->xy[13] = 7;
	s->xy[14] = 11;  s->xy[15] = 11;
	s->xy[16] = 4;  s->xy[17] = 11;
	s->xy[18] = 4;  s->xy[19] = 16;
	s->xy[20] = 11;  s->xy[21] = 16;
	s->xy[22] = 11;  s->xy[23] = 11;
	s->xy[24] = 7;  s->xy[25] = 0;
	s->xy[26] = 11;  s->xy[27] = 0;
	s->xy[28] = 15;  s->xy[29] = 10;
	characters['9'-32] = s;

	/* "C" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 10;
	s->xy[0] = 0;  s->xy[1] = 3;
	s->xy[2] = 3;  s->xy[3] = 0;
	s->xy[4] = 12;  s->xy[5] = 0;
	s->xy[6] = 15;  s->xy[7] = 4;
	s->xy[8] = 4;  s->xy[9] = 4;
	s->xy[10] = 4;  s->xy[11] = 16;
	s->xy[12] = 15;  s->xy[13] = 16;
	s->xy[14] = 11;  s->xy[15] = 20;
	s->xy[16] = 3;  s->xy[17] = 20;
	s->xy[18] = 0;  s->xy[19] = 17;
	characters['C'-32] = s;

	/* "L" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 6;
	s->xy[0] = 0;  s->xy[1] = 0;
	s->xy[2] = 15;  s->xy[3] = 0;
	s->xy[4] = 15;  s->xy[5] = 4;
	s->xy[6] = 4;  s->xy[7] = 4;
	s->xy[8] = 4;  s->xy[9] = 20;
	s->xy[10] = 0;  s->xy[11] = 20;
	characters['L'-32] = s;

	/* "R" */
	s = memory_allocate(sizeof(Character), NULL);
	s->np = 15;
	s->xy[0] = 0;  s->xy[1] = 0;
	s->xy[2] = 4;  s->xy[3] = 0;
	s->xy[4] = 4;  s->xy[5] = 16;
	s->xy[6] = 11;  s->xy[7] = 16;
	s->xy[8] = 11;  s->xy[9] = 11;
	s->xy[10] = 4;  s->xy[11] = 11;
	s->xy[12] = 4;  s->xy[13] = 7;
	s->xy[14] = 8;  s->xy[15] = 7;
	s->xy[16] = 11;  s->xy[17] = 0;
	s->xy[18] = 15;  s->xy[19] = 0;
	s->xy[20] = 12;  s->xy[21] = 8;
	s->xy[22] = 15;  s->xy[23] = 10;
	s->xy[24] = 15;  s->xy[25] = 18;
	s->xy[26] = 12;  s->xy[27] = 20;
	s->xy[28] = 0;  s->xy[29] = 20;
	characters['R'-32] = s;

	/****
	s = memory_allocate(sizeof(Character), NULL);
	s->np = ;
	s->xy[0] = ;  s->xy[1] = ;
	s->xy[2] = ;  s->xy[3] = ;
	s->xy[4] = ;  s->xy[5] = ;
	s->xy[6] = ;  s->xy[7] = ;
	s->xy[8] = ;  s->xy[9] = ;
	s->xy[10] = ;  s->xy[11] = ;
	s->xy[12] = ;  s->xy[13] = ;
	s->xy[14] = ;  s->xy[15] = ;
	s->xy[16] = ;  s->xy[17] = ;
	s->xy[18] = ;  s->xy[19] = ;
	s->xy[20] = ;  s->xy[21] = ;
	s->xy[22] = ;  s->xy[23] = ;
	s->xy[24] = ;  s->xy[25] = ;
	s->xy[26] = ;  s->xy[27] = ;
	s->xy[28] = ;  s->xy[29] = ;
	characters['R'-32] = s;

	****/

}

/**
 * Returns the polygon of the char c, with 32<=c<=127.
 * The char is translated to (x,y) before m be applied.
 * Since the main axis of the runway is along the x-axis, by default
 * the char is rotated accordingly. If upside_down is TRUE (i.e. != 0)
 * the char is rotated 180 DEG.
 */
static VPolygon *
BuildChar(int c, VMatrix * m, double x, double y, int upside_down)
{
	VPoint p, pts[MAX_POINTS_PER_CHAR];
	Character *s;
	int i, np, *xy;
	VPolygon *poly;

	if ( c < 32 || c > 127 ) {
		fprintf(stderr, "BuildChar(): invalid char code %d\n", c);
		return NULL;
	}

	s = characters[c-32];
	if ( s == NULL )
		s = characters[CHAR_DEFAULT-32];

	np = s->np;
	xy = &(s->xy[0]);

	for ( i=0; i<np; i++ ){
		if ( upside_down ) {
			p.y = y + CHAR_SCALE_X * *(xy++);
			p.x = x + CHAR_SCALE_Y * *(xy++);
		}
		else {
			p.y = y - CHAR_SCALE_X * *(xy++);
			p.x = x - CHAR_SCALE_Y * *(xy++);
		}
		p.z = 0.0;
		VTransform(&p, m, &pts[i]);
	}
	poly = VCreatePolygon(np, pts, whitePaintColor);
	poly->flags |= PolyUseCullDistance;
	poly->cullDistance = RUNWAY_CULL_DISTANCE;
	return poly;
}


/**
 * Returns the string length in meters.
 */
static double
StringWidth(char *s)
{
	int l;

	l = strlen(s);
	return l*CHAR_WIDTH + (l-1) * CHAR_MARGIN_X;
}


/**
 * Add the string s to the set of polygons.
 * ox is the distance of the line base from the runway end.
 * oy is the center of the string, typically 0.0.
 */
static void
AddBigString(char *s, VMatrix * m, double ox, double oy, int upside_down,
	VPolySet *ps)
{
	VPolygon *p;
	int len, j;
	double x, y;

	if ( s == NULL )  return;
	len = strlen(s);
	if ( len == 0 )  return;
	x = ox;
	if ( upside_down )
		y = oy - StringWidth(s) / 2.0;
	else
		y = oy + StringWidth(s) / 2.0;

	for ( j=0; j<len; j++ ) {
		p = BuildChar(s[j], m, x, y, upside_down);
		if ( p != NULL )
			VPolySet_Add(ps, p);
		if ( upside_down ) {
			y = y + CHAR_MARGIN_X + CHAR_WIDTH;
		}
		else {
			y = y - CHAR_MARGIN_X - CHAR_WIDTH;
		}
	}
}


/**
 * Parse the runway name, max 7 char long. For example id="15L/33"
 * would set hdg1="15", side1="L", hdg2="33", side2="". If the
 * id can't be parsed successfully, all the strings are set to the
 * empty string and a proper error is displayed.
 * 
 * heading: the runway heading, geographical, normalized to [0,pi[.
 */
static void SplitRunwayName(char *id, double heading,
	char *hdg1, char *side1, char *hdg2, char *side2)
{
	char *c;
	int i;

	hdg1[0] = 0;  side1[0] = 0;
	hdg2[0] = 0;  side2[0] = 0;

	if ( id == NULL ) {
		return;
	} else if ( strlen(id) > 7 ) {
		fprintf(stderr, "Runway name `%s' too long. Max 7 char allowed.\n", id);
		return;
	}

	c = id;

	/* Parse hdg1: */
	i = 0;
	while( isdigit(*c) ){
		hdg1[i] = *c;
		i++;
		c++;
	}
	hdg1[i] = 0;

	/* Parse side1: */
	if( *c != 0 && *c != '/' ){
		i = 0;
		do {
			side1[i] = *c;
			i++;
			c++;
		} while( *c != 0 && *c != '/' );
		side1[i] = 0;
	}

	/* Parse hdg2: */
	if( *c == 0 ){
		fprintf(stderr, "Invalid runway name `%s', missing `/'.\n", id);
		return;
	}
	if( *c != '/' ){
		fprintf(stderr, "Invalid character `%c' in runway name `%s'.\n", *c, id);
		return;
	}
	c++;
	i = 0;
	while( *c != 0 && isdigit(*c) ){
		hdg2[i] = *c;
		i++;
		c++;
	}
	hdg2[i] = 0;

	/* Parse side2: */
	i = 0;
	while( *c != 0 ){
		side2[i] = *c;
		i++;
		c++;
	}
	side2[i] = 0;

	/* Sort labels: */
	int n1 = atoi(hdg1);
	int n2 = atoi(hdg2);
	if( n1 > n2 ){
		char s[10];
		strcpy(s, hdg1);   strcpy(hdg1, hdg2);   strcpy(hdg2, s);
		strcpy(s, side1);   strcpy(side1, side2);   strcpy(side2, s);
		int t = n1; n1 = n2; n2 = t;
	}

	/* Consistency check: */
	if( !(0 <= n1 && n1 <= 36 && 0 <= n2 && n2 <= 36) )
		fprintf(stderr, "Invalid runway numbers `%s', out of the range [0,36]\n", id);
	if( n2 - n1 != 18 )
		fprintf(stderr, "Incompatible numbers in runway name `%s', they must differ by 18, corresponding to 180 DEG.\n", id);

	/*
		The first label will be displayed at the "start" of the runway,
		that is at -0.5*length*(cos(heading),sin(heading), 0.0) in NED,
		whereas the second label would be displayed at the "end" of the
		runway, that is at 0.5*length*(cos(heading),sin(heading), 0.0)
		in NED.

		However there is an issue for runways whose heading is near the
		north-south direction when the magnetic variation is involved,
		because the heading refers to the geographical coords. whereas
		the labels refer to magnetic coords. So, for example, the
		heading of the runway 18/36 may be either 10 DEG or 170 DEG. In
		the first case the labels must be exchanged. A similar issue
		happens for runways whose heading is near suoth. This fix works
		for magnetic variations up to 45 DEG:
	*/
	int h = (int)(units_RADtoDEG(heading));
	if( ( (h <= 45) && (n1 > 13) )
	|| ( (h >= 135) && (n1 < 9) ) ){
		char s[10];
		strcpy(s, hdg1);   strcpy(hdg1, hdg2);   strcpy(hdg2, s);
		strcpy(s, side1);   strcpy(side1, side2);   strcpy(side2, s);
		int t = n1; n1 = n2; n2 = t;
	}

}


static VPolygon * build_rectangle(double x1, double y1, double x2, double y2,
	VColor_Type * color, int cull_distance, VMatrix *m)
{
	VPolygon *p;
	int i;
	VPoint v[4];

	VSetPoint(&v[0], x1, y1, 0.0);
	VSetPoint(&v[1], x2, y1, 0.0);
	VSetPoint(&v[2], x2, y2, 0.0);
	VSetPoint(&v[3], x1, y2, 0.0);

	for( i=0; i < 4; i++ )
		VTransform(&v[i], m, &v[i]);
	
	p = VCreatePolygon(4, v, color);
	p->flags |= PolyUseCullDistance;
	p->cullDistance = cull_distance;

	return p;
}


static void
AddSymmetricPolygons(VMatrix * RWYtoXYZ, double x, double y, int count,
	 double length, double width, double margin, VPolySet *ps)
{
	for (; count > 0; --count, y -= width + margin) {

		VPolySet_Add(ps, build_rectangle(
			x, y,
			x - length, y - width,
			whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

		/*
		 *  Opposite stripe on same runway end
		 */

		VPolySet_Add(ps, build_rectangle(
			x, -y,
			x - length, -y + width,
			whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

		/*
		 *  Stripe on opposite runway end
		 */

		VPolySet_Add(ps, build_rectangle(
			-x, y,
			-x + length, y - width,
			whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

		/*
		 *  Opposite stripe on opposite runway end
		 */

		VPolySet_Add(ps, build_rectangle(
			-x, -y,
			-x + length, -y + width,
			whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

	}
}


VPolySet *
runway_build(char *id, VMatrix * RWYtoXYZ, double length, double width,
	double heading)
{
	double    start, stop, x, delta_x, y;
	int       n, i, stripes;
	VPolySet *ps;
	

	/*
	 * Init module.
	 */
	if ( characters == NULL ) {
		InitBuildChar();
		runwayColor = VColor_getByName(RUNWAY_COLOR, 1);
		whitePaintColor = VColor_getByName(WHITE_PAINT, 1);
		memory_registerCleanup(runway_cleanup);
	}

	ps = VPolySet_New();

/*
 *  First, add the runway surface polygon, a rectangle spanning
 *  on x=[-length/2,lenght/2] and y=[-height/2,height/2].
 *  Longer runways are splitted in several shorter pieces for
 *  better haze rendering.
 */

	if( length <= 500.0 ){
		n = 1;
		delta_x = length;
	} else {
		n = (int) ceil(length / 500.0);
		delta_x = length / n;
	}

	x = -length/2;

	for( i = 0; i < n; i++ ){

		VPolySet_Add(ps, build_rectangle(
			x, width/2, x + delta_x, -width/2,
			runwayColor, RUNWAY_CULL_DISTANCE, RWYtoXYZ ) );

		x += delta_x;

	}

	/*
	 *  Runway numbers, both ends
	 */

	start = -0.5 * length + CHAR_X_OFFSET;
	stop = 0.5 * length - CHAR_X_OFFSET;

	if ( width >= 2.0 * CHAR_WIDTH + CHAR_MARGIN_Y ) {

		char hdg1[8], side1[8], hdg2[8], side2[8];

		SplitRunwayName(id, heading,     hdg1, side1, hdg2, side2);

		if ( strlen(side1) > 0 ) {
			AddBigString(side1, RWYtoXYZ, start, 0.0, 1, ps);
			start += CHAR_HEIGHT + CHAR_MARGIN_Y;
		}
		AddBigString(hdg1, RWYtoXYZ, start, 0.0, 1, ps);
		start += CHAR_HEIGHT;

		if ( strlen(side2) > 0 ) {
			AddBigString(side2, RWYtoXYZ, stop, 0.0, 0, ps);
			stop -= CHAR_HEIGHT + CHAR_MARGIN_Y;
		}
		AddBigString(hdg2, RWYtoXYZ, stop, 0.0, 0, ps);
		stop -= CHAR_HEIGHT;

	}

	/*
	 *  Now the runway centerline markings.
	 */

	start += 12.0 + CENTERLINE_LENGTH / 2.0;
	stop -= 12.0 + CENTERLINE_LENGTH / 2.0;

	for (x = start; x < stop; x += CENTERLINE_LENGTH + CENTERLINE_GAP) {

		VPolySet_Add(ps, build_rectangle(
			x - CENTERLINE_LENGTH / 2.0, CENTERLINE_WIDTH / 2.0,
			x + CENTERLINE_LENGTH / 2.0, -CENTERLINE_WIDTH / 2.0,
			whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

	}

	/*
	 * Runway threshold stripes
	 */

	/* From the Aeronautical Information Manual */

	if (width >= 18.0) {

		stripes = 0;

		if (width >= 60.0) {
			stripes = 16;
		}
		else if (width >= 45.0) {
			stripes = 12;
		}
		else if (width >= 30.0) {
			stripes = 8;
		}
		else if (width >= 23.0) {
			stripes = 6;
		}
		else if (width >= 18.0) {
			stripes = 4;
		}

		stripes >>= 1;

		x = 0.5 * length - THRESHOLD_STRIPE_X_OFFSET;
		y = width / 2.0 - THRESHOLD_STRIPE_Y_OFFSET;

		for (; stripes > 0; --stripes,
			y -= THRESHOLD_STRIPE_WIDTH + THRESHOLD_STRIPE_MARGIN) {

			VPolySet_Add(ps, build_rectangle(
				x, y,
				x - THRESHOLD_STRIPE_LENGTH, y - THRESHOLD_STRIPE_WIDTH,
				whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

			/*
			 *  Opposite stripe on same runway end
			 */

			VPolySet_Add(ps, build_rectangle(
				x, -y,
				x - THRESHOLD_STRIPE_LENGTH, -y + THRESHOLD_STRIPE_WIDTH,
				whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

			/*
			 *  Stripe on opposite runway end
			 */

			VPolySet_Add(ps, build_rectangle(
				-x, y,
				-x + THRESHOLD_STRIPE_LENGTH, y - THRESHOLD_STRIPE_WIDTH,
				whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

			/*
			 *  Opposite stripe on opposite runway end
			 */

			VPolySet_Add(ps, build_rectangle(
				-x, -y,
				-x + THRESHOLD_STRIPE_LENGTH, -y + THRESHOLD_STRIPE_WIDTH,
				whitePaintColor, MARKING_CULL_DISTANCE, RWYtoXYZ ) );

		}

	}

	/*
	 *  Fixed distance marker
	 */

	if (length > units_FEETtoMETERS(3000.0)) {

		AddSymmetricPolygons(RWYtoXYZ,
			0.5 * length - FIXED_MARKER_X_OFFSET,
			0.5 * width - FIXED_MARKER_Y_OFFSET,
			1,
			FIXED_MARKER_LENGTH, FIXED_MARKER_WIDTH, 0.0,
			ps);
	}

	return ps;
}
