Blob Blame Raw
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;

namespace Assistance {
	public class Track {
		public static readonly Pen pen = new Pen(Brushes.DarkGreen, 3f);
		public static readonly Pen penPreview = new Pen(new SolidBrush(Color.FromArgb(64, Color.DarkGreen)), 1f);
	
		public readonly List<Point> points = new List<Point>();

		private readonly List<Track> parents = new List<Track>();
		private readonly List<Geometry.TransformFunc> transformFuncs = new List<Geometry.TransformFunc>();

		public Track() { }

		public Track(Track parent, Geometry.TransformFunc transformFunc):
			this()
		{
			parents.AddRange(parent.parents);
			parents.Add(parent);
			transformFuncs.AddRange(parent.transformFuncs);
			transformFuncs.Add(transformFunc);
		}

		public Track(Track parent, Geometry.TransformFunc transformFunc, double precision):
			this(parent, transformFunc)
		{
			rebuild(precision);
		}

		public Track createChild(Geometry.TransformFunc transformFunc){
			return new Track(this, transformFunc);
		}

		public Track createChildAndBuild(Geometry.TransformFunc transformFunc, double precision = 1.0) {
			return new Track(this, transformFunc, precision);
		}

		public Rectangle getBounds() {
			if (points.Count == 0)
				return new Rectangle();
			Rectangle bounds = new Rectangle(points[0]);
			foreach(Point p in points)
				bounds = bounds.expand(p);
			return bounds.inflate(Math.Max(pen.Width, penPreview.Width) + 2.0);
		}

		public Point transform(Point p) {
			return Geometry.transform(transformFuncs, p);
		}
				
		private void addSpline(Point p0, Point p1, Point t0, Point t1, Point tp0, Point tp1, double l0, double l1, double precisionSqr) {
			if ((tp1 - tp0).lenSqr() < precisionSqr) {
				points.Add(tp1);
			} else {
				double l = 0.5*(l0 + l1);
				Point p = Geometry.splinePoint(p0, p1, t0, t1, l);
				Point tp = transform(p);
				addSpline(p0, p1, t0, t1, tp0, tp, l0, l, precisionSqr);
				addSpline(p0, p1, t0, t1, tp, tp1, l, l1, precisionSqr);
			}
		}
		
		public void rebuild(double precision = 1.0) {
			if (parents.Count == 0) return;
			
			points.Clear();
			
			Track root = parents[0];
			if (root.points.Count < 2) {
				foreach(Point p in root.points)
					points.Add( Geometry.transform(transformFuncs, p) );
				return;
			}
			
			double precisionSqr = precision * precision;
			Point p0 = root.points[0];
			Point p1 = root.points[1];
			Point t0 = 0.5*(p1 - p0);
			Point tp0 = Geometry.transform(transformFuncs, p0);
			points.Add(tp0);
			for(int i = 1; i < root.points.Count; ++i) {
				Point p2 = root.points[i+1 < root.points.Count ? i+1 : i];
				Point tp1 = Geometry.transform(transformFuncs, p1);
				Point t1 = 0.5*(p2 - p0);
				addSpline(p0, p1, t0, t1, tp0, tp1, 0.0, 1.0, precisionSqr);

				p0 = p1;
				p1 = p2;
				tp0 = tp1;
				t0 = t1;
			}
		}

		public void draw(Graphics g, bool preview = false) {
			if (points.Count < 2)
				return;
			PointF[] ps = new PointF[points.Count];
			for(int i = 0; i < ps.Length; ++i)
				ps[i] = points[i].toFloat();
			g.DrawLines(preview ? penPreview : pen, ps);
		}
	}
}