using System; using System.Collections.Generic; using Assistance.Drawing; namespace Assistance { public class Track { public static readonly Pen pen = new Pen("Dark Green", 3.0); public static readonly Pen penSpecial = new Pen("Blue", 3.0); public static readonly Pen penPreview = new Pen("Dark Green", 1.0, 0.25); private static long lastTouchId; public long touchId; public readonly Gdk.Device device; public readonly List points = new List(); private readonly List parents = new List(); private readonly List transformFuncs = new List(); public static long getTouchId() { return ++lastTouchId; } public Track(long touchId, Gdk.Device device) { this.touchId = touchId; this.device = device; } public Track(Track parent, Geometry.TransformFunc transformFunc): this(parent.touchId, parent.device) { 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].point); foreach(TrackPoint p in points) bounds = bounds.expand(p.point); return bounds.inflate(Math.Max(pen.width, penPreview.width) + 2.0); } public TrackPoint transform(TrackPoint p) { p.point = Geometry.transform(transformFuncs, p.point); return p; } private void addSpline( TrackPoint p0, TrackPoint p1, TrackPoint t0, TrackPoint t1, TrackPoint tp0, TrackPoint tp1, double l0, double l1, double precisionSqr ) { if ((tp1.point - tp0.point).lenSqr() < precisionSqr) { points.Add(tp1); } else { double l = 0.5*(l0 + l1); TrackPoint p = p0.spawn( Geometry.splinePoint(p0.point, p1.point, t0.point, t1.point, l), p0.time + l*(p1.time - p0.time), Geometry.splinePoint(p0.pressure, p1.pressure, t0.pressure, t1.pressure, l), Geometry.splinePoint(p0.tilt, p1.tilt, t0.tilt, t1.tilt, l) ); TrackPoint 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(TrackPoint p in root.points) points.Add( transform(p) ); return; } double precisionSqr = precision * precision; TrackPoint p0 = root.points[0]; TrackPoint p1 = root.points[1]; TrackPoint t0 = new TrackPoint(); TrackPoint tp0 = transform(p0); points.Add(tp0); for(int i = 1; i < root.points.Count; ++i) { TrackPoint p2 = root.points[i+1 < root.points.Count ? i+1 : i]; TrackPoint tp1 = transform(p1); double dt = p2.time - p0.time; TrackPoint t1 = dt > Geometry.precision ? (p2 - p0)*(p1.time - p0.time)/dt : new TrackPoint(); addSpline(p0, p1, t0, t1, tp0, tp1, 0.0, 1.0, precisionSqr); p0 = p1; p1 = p2; tp0 = tp1; t0 = t1; } } public void draw(Cairo.Context context, bool preview = false) { if (preview) { if (points.Count < 2) return; context.Save(); penPreview.apply(context); context.MoveTo(points[0].point.x, points[0].point.y); for(int i = 1; i < points.Count; ++i) context.LineTo(points[i].point.x, points[i].point.y); context.Stroke(); context.Restore(); } else { context.Save(); pen.apply(context); foreach(TrackPoint p in points) { double t = p.keyState.howLongPressed(Gdk.Key.m) + p.buttonState.howLongPressed(3); double w = p.pressure*pen.width + 5.0*t; context.Arc(p.point.x, p.point.y, 2.0*w, 0.0, 2.0*Math.PI); context.Fill(); } context.Restore(); } } } }