Blame mono/Assistance/Track.cs

cfceb0
using System;
cfceb0
using System.Collections.Generic;
b82ef4
using Assistance.Drawing;
cfceb0
cfceb0
namespace Assistance {
cfceb0
	public class Track {
589f9a
		public interface IOwner { }
61bedf
a62e15
		public class Handler {
589f9a
			public readonly IOwner owner;
a62e15
			public readonly Track original;
a62e15
			public readonly List<track> tracks = new List<track>();
589f9a
			public Handler(IOwner owner, Track original) {
a62e15
				this.owner = owner;
a62e15
				this.original = original;
a62e15
			}
a62e15
		}
a62e15
a62e15
		public class Modifier {
a62e15
			public readonly Handler handler;
a62e15
			public readonly double timeOffset;
a62e15
a62e15
			public Modifier(Handler handler, double timeOffset = 0.0)
a62e15
				{ this.handler = handler; this.timeOffset = timeOffset; }
589f9a
			public IOwner owner
a62e15
				{ get { return handler.owner; } }
a62e15
			public Track original
a62e15
				{ get { return handler.original; } }
702257
			public virtual Point calcPoint(double originalIndex) {
702257
				Point p = original.calcPoint(originalIndex);
7bbf70
				p.originalIndex = originalIndex;
7bbf70
				return p;
7bbf70
			}
a62e15
		}
a62e15
a62e15
		public struct Point {
a62e15
			public Assistance.Point position;
a62e15
			public double pressure;
589f9a
			public Assistance.Point tilt;
a62e15
a62e15
			public double originalIndex;
a62e15
			public double time;
a62e15
			public double length;
a62e15
			
a62e15
			public bool final;
a62e15
	
702257
			public Point(
702257
				Assistance.Point position,
702257
				double pressure = 0.5,
702257
				Assistance.Point tilt = new Assistance.Point(),
a62e15
				double originalIndex = 0.0,
a62e15
				double time = 0.0,
a62e15
				double length = 0.0,
a62e15
				bool final = false
a62e15
			) {
702257
				this.position = position;
702257
				this.pressure = pressure;
702257
				this.tilt = tilt;
a62e15
				this.originalIndex = originalIndex;
a62e15
				this.time = time;
a62e15
				this.length = length;
a62e15
				this.final = final;
a62e15
			}
a62e15
		}
a62e15
		
77e178
61bedf
		private static long lastId;
72d2fd
		private readonly List<point> privatePoints = new List<point>();</point></point>
72d2fd
		private int privatePointsRemoved;
72d2fd
		private int privatePointsAdded;
77e178
		
61bedf
		public readonly long id;
c66393
		public readonly Gdk.Device device;
61bedf
		public readonly long touchId;
a62e15
		public readonly KeyHistory<gdk.key>.Holder keyHistory;</gdk.key>
a62e15
		public readonly KeyHistory<uint>.Holder buttonHistory;</uint>
cfceb0
a62e15
		public readonly Modifier modifier;
a62e15
		
a62e15
		public Handler handler;
77c1c7
a62e15
		public Track(
61bedf
			Gdk.Device device = null,
61bedf
			long touchId = 0,
61bedf
			KeyHistory<gdk.key>.Holder keyHistory = null,</gdk.key>
61bedf
			KeyHistory<uint>.Holder buttonHistory = null</uint>
a62e15
		) {
61bedf
			this.id = ++lastId;
c66393
			this.device = device;
61bedf
			this.touchId = touchId;
a62e15
			this.keyHistory = keyHistory;
a62e15
			this.buttonHistory = buttonHistory;
c66393
		}
a62e15
		
61bedf
		public Track(Modifier modifier):
a62e15
			this( modifier.original.device,
589f9a
				  modifier.original.touchId,
a62e15
				  modifier.original.keyHistory.offset( modifier.timeOffset ),
61bedf
				  modifier.original.buttonHistory.offset( modifier.timeOffset ) )
a62e15
			{ this.modifier = modifier; }
77c1c7
a62e15
		public Track original
a62e15
			{ get { return modifier != null ? modifier.original : null; } }
a62e15
		public double timeOffset
a62e15
			{ get { return modifier != null ? modifier.timeOffset : 0.0; } }
720280
		public long ticks
720280
			{ get { return keyHistory.ticks; } }
c3ebff
			
72d2fd
		public int pointsAdded
72d2fd
			{ get { return privatePointsAdded; } }
72d2fd
		public int pointsRemoved
72d2fd
			{ get { return privatePointsRemoved; } }
72d2fd
		public void resetRemoved()
72d2fd
			{ privatePointsRemoved = 0; }
72d2fd
		public void resetAdded()
72d2fd
			{ privatePointsAdded = 0; }
72d2fd
		public void resetCounters()
72d2fd
			{ resetRemoved(); resetAdded(); }
72d2fd
		public void forceRemoved(int pointsRemoved)
72d2fd
			{ privatePointsRemoved = pointsRemoved; }
72d2fd
		public void forceAdded(int pointsAdded)
72d2fd
			{ privatePointsAdded = pointsAdded; }
72d2fd
		
72d2fd
		public bool wasRemoved
72d2fd
			{ get { return pointsRemoved > 0; } }
72d2fd
		public bool wasAdded
72d2fd
			{ get { return pointsAdded > 0; } }
72d2fd
		public bool wasChanged
72d2fd
			{ get { return wasRemoved || wasAdded; } }
72d2fd
72d2fd
		public bool isEmpty
72d2fd
			{ get { return count == 0; } }
72d2fd
		public int count
72d2fd
			{ get { return privatePoints.Count; } }
72d2fd
		public void remove(int count = 1) {
72d2fd
			if (count > this.count) count = this.count;
72d2fd
			if (count <= 0) return;
72d2fd
			privatePoints.RemoveRange(this.count - count, count);
72d2fd
			privatePointsRemoved += count;
72d2fd
		}
72d2fd
		public void truncate(int count)
72d2fd
			{ remove(this.count - count); }
72d2fd
		public void add(Point p) {
72d2fd
			if (!isEmpty) {
72d2fd
				Point previous = getLast();
72d2fd
				// fix originalIndex
72d2fd
				if (p.originalIndex < previous.originalIndex)
72d2fd
					p.originalIndex = previous.originalIndex;
72d2fd
				// fix time
72d2fd
				p.time = Math.Max(p.time, previous.time + Timer.step);
72d2fd
				// calculate length
72d2fd
				p.length = previous.length + (p.position - previous.position).len();
72d2fd
			}
72d2fd
			privatePoints.Add(p);
72d2fd
			++privatePointsAdded;
72d2fd
		}
72d2fd
		
72d2fd
		public Point this[int index]
72d2fd
			{ get { return getPoint(index); } }
72d2fd
		public IEnumerable<point> points</point>
72d2fd
			{ get { return privatePoints; } }
77c1c7
a62e15
		public Track getRoot()
a62e15
			{ return original == null ? this : original.getRoot(); }
a62e15
		public int getLevel()
a62e15
			{ return original == null ? 0 : original.getLevel() + 1; }
c66393
		
702257
		public Point getFirst()
702257
			{ return getPoint(0); }
702257
		public Point getLast()
72d2fd
			{ return getPoint(count - 1); }
a62e15
		public bool isFinished()
72d2fd
			{ return count > 0 && getLast().final; }
a62e15
		
a62e15
		public int clampIndex(int index)
72d2fd
			{ return Math.Min(Math.Max(index, 0), count - 1); }
a62e15
		public int floorIndex(double index, out double frac) {
a62e15
			int i = (int)Math.Floor(index + Geometry.precision);
72d2fd
			if (i > count - 1)
72d2fd
				{ frac = 0.0; return count - 1; }
a62e15
			if (i < 0)
a62e15
				{ frac = 0.0; return 0; }
a62e15
			frac = Math.Max(0.0, index - (double)i);
a62e15
			return i;
72b17c
		}
1cdf17
		public int floorIndexNoClamp(double index)
1cdf17
			{ return (int)Math.Floor(index + Geometry.precision); }
a62e15
		public int floorIndex(double index)
1cdf17
			{ return clampIndex(floorIndexNoClamp(index)); }
1cdf17
		public int ceilIndexNoClamp(double index)
1cdf17
			{ return (int)Math.Ceiling(index - Geometry.precision); }
a62e15
		public int ceilIndex(double index)
1cdf17
			{ return clampIndex(ceilIndexNoClamp(index)); }
a62e15
		
702257
		public Point getPoint(int index) {
a62e15
			index = clampIndex(index);
72d2fd
			return index < 0 ? new Point() : privatePoints[index];
77c1c7
		}
702257
		public Point floorPoint(double index, out double frac)
702257
			{ return getPoint(floorIndex(index, out frac)); }
702257
		public Point floorPoint(double index)
702257
			{ return getPoint(floorIndex(index)); }
702257
		public Point ceilPoint(double index)
702257
			{ return getPoint(ceilIndex(index)); }
77c1c7
		
702257
		private delegate double PointFieldGetter(Point p);
702257
		private double binarySearch(double value, PointFieldGetter getter) {
a62e15
			// points[a].value <= value < points[b].value
77c1c7
			
72d2fd
			if (isEmpty) return 0.0;
a62e15
			int a = 0;
72d2fd
			double aa = getter(privatePoints[a]);
72d2fd
			if (value - aa <= 0.5*Geometry.precision) return (double)a;
a62e15
72d2fd
			int b = count - 1;
72d2fd
			double bb = getter(privatePoints[b]);
72d2fd
			if (bb - value <= 0.5*Geometry.precision) return (double)b;
77c1c7
			
a62e15
			while(true) {
a62e15
				int c = (a + b)/2;
a62e15
				if (a == c) break;
72d2fd
				double cc = getter(privatePoints[c]);
72d2fd
				if (cc - value > 0.5*Geometry.precision)
a62e15
					{ b = c; bb = cc; } else { a = c; aa = cc; }
77c1c7
			}
a62e15
72d2fd
			return bb - aa >= 0.5*Geometry.precision ? (double)a + (value - aa)/(bb - aa) : (double)a;
a62e15
		}
77c1c7
			
a62e15
		public double indexByOriginalIndex(double originalIndex)
702257
			{ return binarySearch(originalIndex, delegate(Point p) { return p.originalIndex; }); }
a62e15
		public double indexByTime(double time)
702257
			{ return binarySearch(time, delegate(Point p) { return p.time; }); }
a62e15
		public double indexByLength(double length)
702257
			{ return binarySearch(length, delegate(Point p) { return p.length; }); }
a62e15
		
a62e15
		public double originalIndexByIndex(double index) {
a62e15
			double frac;
702257
			Point p0 = floorPoint(index, out frac), p1 = ceilPoint(index);
a62e15
			return Geometry.interpolationLinear(p0.originalIndex, p1.originalIndex, frac);
a62e15
		}
a62e15
		public double timeByIndex(double index) {
a62e15
			double frac;
702257
			Point p0 = floorPoint(index, out frac), p1 = ceilPoint(index);
a62e15
			return Geometry.interpolationLinear(p0.time, p1.time, frac);
a62e15
		}
a62e15
		public double lengthByIndex(double index) {
a62e15
			double frac;
702257
			Point p0 = floorPoint(index, out frac), p1 = ceilPoint(index);
a62e15
			return Geometry.interpolationLinear(p0.length, p1.length, frac);
77c1c7
		}
cfceb0
702257
		public Point calcPoint(double index) {
a62e15
			return modifier == null
702257
			     ? interpolateLinear(index)
702257
			     : modifier.calcPoint( originalIndexByIndex(index) );
a62e15
		}
a62e15
		
72d2fd
		public Assistance.Point calcTangent(double index, double distance = 0.1) {
72d2fd
			double minDistance = 10.0*Geometry.precision;
72d2fd
			if (distance < minDistance) distance = minDistance;
72d2fd
			Point p = calcPoint(index);
72d2fd
			Point pp = calcPoint(indexByLength(p.length - distance));
72d2fd
			Assistance.Point dp = p.position - pp.position;
72d2fd
			double lenSqr = dp.lenSqr();
72d2fd
			return lenSqr > Geometry.precisionSqr ? dp*Math.Sqrt(1.0/lenSqr) : new Assistance.Point();
72d2fd
		}
72d2fd
		
702257
		public Point interpolateLinear(double index) {
a62e15
			double frac;
702257
			Point p0 = floorPoint(index, out frac);
702257
			Point p1 = ceilPoint(index);
702257
			return interpolateLinear(p0, p1, frac);
a62e15
		}
a62e15
702257
		public static Point interpolateLinear(Point p0, Point p1, double l) {
a62e15
			if (l <= Geometry.precision) return p0;
a62e15
			if (l >= 1.0 - Geometry.precision) return p1;
702257
			return new Point(
702257
				Geometry.interpolationLinear(p0.position, p1.position, l),
702257
				Geometry.interpolationLinear(p0.pressure, p1.pressure, l),
702257
				Geometry.interpolationLinear(p0.tilt, p1.tilt, l),
a62e15
				Geometry.interpolationLinear(p0.originalIndex, p1.originalIndex, l),
a62e15
				Geometry.interpolationLinear(p0.time, p1.time, l),
702257
				Geometry.interpolationLinear(p0.length, p1.length, l) );
cfceb0
		}
7bbf70
		
7bbf70
		public void print() {
72d2fd
			foreach(Point wp in privatePoints)
7bbf70
				Console.Write(
7bbf70
					"{2:f1}, ",
702257
					wp.position.x,
702257
					wp.position.y,
7bbf70
					wp.originalIndex );
7bbf70
			Console.WriteLine();
7bbf70
		}
72d2fd
72d2fd
		public void verify(string message) {
72d2fd
			bool error = false;
72d2fd
			for(int i = 1; i < count; ++i) {
72d2fd
				Point pp = privatePoints[i-1];
72d2fd
				Point p = privatePoints[i];
72d2fd
				if ( Geometry.isGreater(pp.originalIndex, p.originalIndex)
72d2fd
				  /*|| Geometry.isGreater(pp.length, p.length)
72d2fd
				  || Geometry.isGreater(pp.time, p.time)
72d2fd
				  || pp.final*/ )
72d2fd
				{
72d2fd
					if (!error) Console.WriteLine("Track error: " + message);
72d2fd
					error = true;
72d2fd
					Console.WriteLine("--- index: " + (i-1) + " / " + i);
72d2fd
					Console.WriteLine("    originalIndex: " + pp.originalIndex + " / " + p.originalIndex);
72d2fd
					Console.WriteLine("    length: " + pp.length + " / " + p.length);
72d2fd
					Console.WriteLine("    time: " + pp.time + " / " + p.time);
72d2fd
					Console.WriteLine("    final: " + pp.final + " / " + p.final);
72d2fd
				}	
72d2fd
			}
72d2fd
			if (error) print();
72d2fd
		}
cfceb0
	}
cfceb0
}
cfceb0