// Computes the tangents of the control points of a Catmull Rom spline
		private void ComputeTangents(ref CatmullRomSplineData spline)
		{
			float f = 2.0f;
			int i;

			if (spline.tangents == null)
				spline.tangents = new List<Vector2D>();
			else
				spline.tangents.Clear();

			// Special case for the first control point
			// spline.tangents.Add(new Vector2D((spline.controlpoints[1] - spline.controlpoints[0]) / f));
			spline.tangents.Add(new Vector2D(spline.controlpoints[1] - spline.controlpoints[0]));

			for (i=1; i < spline.controlpoints.Count - 1; i++)
			{
				spline.tangents.Add(new Vector2D((spline.controlpoints[i + 1] - spline.controlpoints[i - 1]) / f));
			}

			// Special case for the last control point
			//spline.tangents.Add(new Vector2D((spline.controlpoints[i] - spline.controlpoints[i - 1]) / f));
			spline.tangents.Add(new Vector2D(spline.controlpoints[i] - spline.controlpoints[i - 1]));
		}
		private void InitializeCatmullRomSplines()
		{
			List<Linedef> sourceld = new List<Linedef>(General.Map.Map.GetSelectedLinedefs(true));
			Line2D innerline, outerline;
			CatmullRomSplineData innerspline, outerspline;
			CatmullRomSplineGroup splinegroup;
			
			numcontrolpoints = stairsectorbuilderform.NumControlPoints;

			if (General.Map.Map.GetSelectedLinedefs(true).Count <= 0)
			{
				return;
			}

			if (catmullromsplinegroups == null)
				catmullromsplinegroups = new List<CatmullRomSplineGroup>();
			else
				catmullromsplinegroups.Clear();

			for (int l1 = 0; l1 < sourceld.Count - 1; l1++)
			{
				int l2 = l1 + 1;
				Vector2D s1, s2, e1, e2;

				s1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].End.Position : sourceld[l1].Start.Position;
				e1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].Start.Position : sourceld[l1].End.Position;
				s2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].End.Position : sourceld[l2].Start.Position;
				e2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].Start.Position : sourceld[l2].End.Position;

				//innerline = new Line2D(sourceld[l2].Start.Position, sourceld[l1].Start.Position);
				//outerline = new Line2D(sourceld[l1].End.Position, sourceld[l2].End.Position);

				innerline = new Line2D(s2, s1);
				outerline = new Line2D(e1, e2);

				innerspline = new CatmullRomSplineData();
				innerspline.controlpoints = new List<Vector2D>();

				innerspline.line = innerline;
				innerspline.controlpoints.Add(new Vector2D(innerline.v1));

				for (int k = 1; k <= numcontrolpoints - 2; k++)
					innerspline.controlpoints.Add(new Vector2D(innerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k)));

				innerspline.controlpoints.Add(new Vector2D(innerline.v2));

				ComputeTangents(ref innerspline);

				outerspline = new CatmullRomSplineData();
				outerspline.controlpoints = new List<Vector2D>();

				outerspline.line = outerline;
				outerspline.controlpoints.Add(new Vector2D(outerline.v1));

				for (int k = 1; k <= numcontrolpoints - 2; k++)
					outerspline.controlpoints.Add(new Vector2D(outerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k)));

				outerspline.controlpoints.Add(new Vector2D(outerline.v2));

				ComputeTangents(ref outerspline);

				splinegroup = new CatmullRomSplineGroup();
				splinegroup.splines = new CatmullRomSplineData[2];
				splinegroup.splines[INNER_SPLINE] = innerspline;
				splinegroup.splines[OUTER_SPLINE] = outerspline;
				splinegroup.sourcelinedef = sourceld[l1];

				catmullromsplinegroups.Add(splinegroup);
			}
		}
		// Redrawing display
		public override void OnRedrawDisplay()
		{
			if (!stairsectorbuilderform.FullyLoaded) return;

			base.OnRedrawDisplay();

			renderer.RedrawSurface();

			// Render lines
			if (renderer.StartPlotter(true))
			{
				renderer.PlotLinedefSet(General.Map.Map.Linedefs);
				renderer.PlotVerticesSet(General.Map.Map.Vertices);
				renderer.Finish();
			}

			// Render things
			if (renderer.StartThings(true))
			{
				renderer.RenderThingSet(General.Map.Map.Things, 1.0f);
				renderer.Finish();
			}

			// Render overlay
			if (renderer.StartOverlay(true))
			{
				if (stairsectorbuilderform.NumberOfSectors > 0)
				{
					if (stairsectorbuilderform.Tabs.SelectedIndex == 2 && numcontrolpoints != stairsectorbuilderform.NumControlPoints)
					{
						ModifyControlpointNumber(stairsectorbuilderform.NumControlPoints);
						numcontrolpoints = stairsectorbuilderform.NumControlPoints;
					}

					// Create sector info based on the settings...
					UpdateVertexData();

					// Render the control points if the Catmull Rom spline tab is selected
					if (stairsectorbuilderform.Tabs.SelectedIndex == 2)
					{
						foreach (List<Vector2D> lv in exactsplines)
						{
							for (int i = 0; i < lv.Count - 1; i++)
							{
								renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true);
							}
						}

						foreach (CatmullRomSplineGroup crsg in catmullromsplinegroups)
						{
							CatmullRomSplineData[] crsd = new CatmullRomSplineData[2] { crsg.splines[INNER_SPLINE], crsg.splines[OUTER_SPLINE] };

							foreach (CatmullRomSplineData c in crsd)
							{
								for (int i = 0; i < c.controlpoints.Count; i++)
								{
									RectangleF rect = new RectangleF(c.controlpoints[i].x - CONTROLPOINT_SIZE / 2, c.controlpoints[i].y - CONTROLPOINT_SIZE / 2, 10.0f, 10.0f);

									if (i == 0) renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true);
									else if (i == c.controlpoints.Count - 1) renderer.RenderRectangle(rect, 2, new PixelColor(128, 0, 255, 0), true);
									else renderer.RenderRectangle(rect, 2, General.Colors.Indication, true);
									//renderer.RenderLine(c.controlpoints[i], c.controlpoints[i] + c.tangents[i], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true);
								}
							}
						}
					}

					// ... and draw the preview
					foreach (StairInfo si in stairsectors)
					{
						foreach (List<Vector2D> lv in si.sectors)
						{
							FlatVertex[] fvl = new FlatVertex[lv.Count];

							for (int i = 0; i < lv.Count - 1; i++)
							{
								FlatVertex fv = new FlatVertex();

								renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, General.Colors.Highlight, true);

								fvl[lv.Count - 1 - i].x = fv.u = lv[i].x;
								fvl[lv.Count - 1 - i].y = fv.v = lv[i].y;
								fvl[lv.Count - 1 - i].z = 1;
								fvl[lv.Count - 1 - i].c = 1056964608;

								RectangleF rect = new RectangleF(lv[i].x - CONTROLPOINT_SIZE / 2, lv[i].y - CONTROLPOINT_SIZE / 2, 10.0f, 10.0f);
								renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true);
							}

							fvl[0] = fvl[lv.Count - 1];

							// renderer.RenderGeometry(fvl, null, true);
						}
					}
				}

				renderer.Finish();
			}
			renderer.Present();
		}
		// Generats the Catmull Rom spline from the given control points and their tangents.
		// Returns a list of Vector2D.
		// The line between two control points is a "section". There is always one section
		// less than there are control points.
		// A "hop" is the distance between two to-be-created vertices. The distance is not
		// in map units, but a relative indication where on the line between two control
		// points the vertex will be.
		// First it is calculated in which section the vertex will be in, then the relative
		// position on the line between the control points of this section is calculated.
		// This value will be from 0.0 to 0.99999...
		// A workaround has to be done for the last vertex, which will inevitably be exactly
		// on the last control point, thus computing the wrong section and relative position
		private List<Vector2D> GenerateCatmullRom(CatmullRomSplineData crsd, int numverts)
		{
			List<Vector2D> vertices = new List<Vector2D>();
			int sections = crsd.controlpoints.Count - 1;
			double hop = (double)sections / numverts;
			float distance = 0.0f;
			float unithop = 0;
			List<float> cpdistance = new List<float>();

			// Compute the length of the whole spline and the length of the parts on the
			// spline between the control points
			for (int i = 0; i < crsd.controlpoints.Count-1; i++)
			{
				int h = (int)Vector2D.Distance(crsd.controlpoints[i], crsd.controlpoints[i + 1]);
				float dist = 0;
				List<Vector2D> dc = new List<Vector2D>();

				Vector2D p0 = crsd.controlpoints[i];
				Vector2D p1 = crsd.controlpoints[i + 1];
				Vector2D t0 = crsd.tangents[i];
				Vector2D t1 = crsd.tangents[i + 1];

				for (int k = 0; k <= h; k++)
				{
					dc.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)(((float)k/(float)h))));
				}

				for (int k = 0; k < h; k++)
				{
					dist += Vector2D.Distance(dc[k], dc[k + 1]);
				}

				distance += dist;

				cpdistance.Add(dist);

				exactsplines.Add(dc);
			}

			unithop = distance / numverts;

			for (int i = 0; i <= numverts; i++)
			{
				int s = 0;
				float u;
				float distancefromstart = i * unithop;
				float max = 0.0f;

				// Find out what section the vertex is in
				while (max < distancefromstart)
				{
					max += cpdistance[s];
					if (max < distancefromstart) s++;

					// Work around for rounding errors
					if (s > cpdistance.Count-1)
					{
						s = cpdistance.Count - 1;
						max = distancefromstart;
					}
				}

				// Compute the u component with special cases for the first
				// and last vertex
				if (distancefromstart == 0)
					u = 0.0f;
				else if (distancefromstart == distance)
					u = 1.0f;
				else
					u = 1.0f - ((max - distancefromstart) / cpdistance[s]);

				Vector2D p0 = crsd.controlpoints[s];
				Vector2D p1 = crsd.controlpoints[s + 1];
				Vector2D t0 = crsd.tangents[s];
				Vector2D t1 = crsd.tangents[s + 1];
				vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, u));
			}

			// OLD CODE
			//for (int x = 0; x <= numverts; x++)
			//{
			//    // Compute the section the vertex will be in
			//    int s = (int)(x * hop);
			//    double u;

			//    // Workaround for the last vertex
			//    if ((x * hop) - s < 0.00001f && s == sections)
			//    {
			//        u = 1.0f;
			//        s--;
			//    }
			//    else
			//    {
			//        //u = (summe - x * unithop) / Vector2D.Distance(crsd.controlpoints[cs], crsd.controlpoints[cs+1]);
			//        u = (x * hop) - s;
			//    }

			//    if (u > 1.0f) u = 1.0f;

			//    Vector2D p0 = crsd.controlpoints[s];
			//    Vector2D p1 = crsd.controlpoints[s + 1];
			//    Vector2D t0 = crsd.tangents[s];
			//    Vector2D t1 = crsd.tangents[s + 1];
			//    vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)u));
			//}

			return vertices;
		}