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

namespace Assistance {
	public class InputModifierTangents: InputManager.Modifier {
		public struct Tangent {
			public Point position;
			public double pressure;
			public Point tilt;
			
			public Tangent(
				Point position,
				double pressure = 0.0,
				Point tilt = new Point() )
			{
				this.position = position;
				this.pressure = pressure;
				this.tilt = tilt;
			}
		}
	
		public class Modifier: Track.Modifier {
			public Modifier(Track.Handler handler):
				base(handler) { }
			
			public InputManager.KeyPoint.Holder holder = null;
			public readonly List<Tangent> tangents = new List<Tangent>();
			
			public override Track.Point calcPoint(double originalIndex) {
				double frac;
				int i0 = original.floorIndex(originalIndex, out frac);
				int i1 = original.ceilIndex(originalIndex);
				Track.Point p = i0 < 0 ? new Track.Point()
				              : interpolateSpline(
									original[i0],
									original[i1],
									i0 < tangents.Count ? tangents[i0] : new Tangent(),
									i1 < tangents.Count ? tangents[i1] : new Tangent(),
									frac );
				p.originalIndex = originalIndex;
				return p;
			}
		}

		public static Track.Point interpolateSpline(Track.Point p0, Track.Point p1, Tangent t0, Tangent t1, double l) {
			if (l <= Geometry.precision) return p0;
			if (l >= 1.0 - Geometry.precision) return p1;
			return new Track.Point(
				Geometry.interpolationSpline(p0.position, p1.position, t0.position, t1.position, l),
				Geometry.interpolationSpline(p0.pressure, p1.pressure, t0.pressure, t1.pressure, l),
				Geometry.interpolationSpline(p0.tilt, p1.tilt, t0.tilt, t1.tilt, l),
				Geometry.interpolationLinear(p0.originalIndex, p1.originalIndex, l),
				Geometry.interpolationLinear(p0.time, p1.time, l),
				Geometry.interpolationLinear(p0.length, p1.length, l) );
		}

		public override void modify(Track track, InputManager.KeyPoint keyPoint, List<Track> outTracks) {
			if (track.handler == null) {
				track.handler = new Track.Handler(this, track);
				track.handler.tracks.Add(new Track( new Modifier(track.handler) ));
			}
			
			Track subTrack = track.handler.tracks[0];
			Modifier modifier = (Modifier)subTrack.modifier;
			outTracks.Add(subTrack);
			
			if ( !track.wasChanged
			  && track.count == subTrack.count
			  && track.count == modifier.tangents.Count )
			  	return;
			
			if (!track.wasChanged && subTrack.count == track.count - 1) {
				// add temporary point
				modifier.tangents.Add(new Tangent());
				subTrack.add(track.getLast());
			} else {
				// apply permanent changes
				
				// remove points
				int start = track.count - track.pointsAdded;
				if (start < 0) start = 0;
				if (start > 1) --start;
				subTrack.truncate(start);
				if (start < modifier.tangents.Count)
					modifier.tangents.RemoveRange(start, modifier.tangents.Count - start);
				
				// add first point
				int index = start;
				if (index == 0) {
					modifier.tangents.Add(new Tangent());
					subTrack.add(track.getLast());
					++index;
				}
				
				// add points with tangents
				if (track.count > 2) {
					while(index < track.count) {
						Track.Point p0 = track[index-1];
						Track.Point p1 = track[index];
						Track.Point p2 = track[index+1];
						double dt = p2.time - p0.time;
						double k = dt > Geometry.precision ? (p1.time - p0.time)/dt : 0.0;
						Tangent tangent = new Tangent(
							(p2.position - p0.position)*k,
							(p2.pressure - p0.pressure)*k,
							(p2.tilt - p0.tilt)*k );
						modifier.tangents.Add(tangent);
						subTrack.add(p1);
						++index;
					}
				}
				
				track.resetCounters();
				
				// release previous key point
				if (modifier.holder != null) {
					modifier.holder.Dispose();
					modifier.holder = null;
				}
				
				if (track.isFinished()) {
					// finish
					modifier.tangents.Add(new Tangent());
					subTrack.add(track.getLast());
				} else {
					// save key point
					modifier.holder = keyPoint.hold();
				}
			}
		}
	}
}