From 77c1c7731cc364d0c35991f74b0cfbe74443272f Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Dec 26 2017 07:24:12 +0000 Subject: assistance: spline interpolation --- diff --git a/mono/Assistance/Geometry.cs b/mono/Assistance/Geometry.cs index 6290b73..91fc0ed 100644 --- a/mono/Assistance/Geometry.cs +++ b/mono/Assistance/Geometry.cs @@ -1,7 +1,12 @@ using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections.Generic; namespace Assistance { public static class Geometry { + public delegate Point TransformFunc(Point p); + public static readonly double precision = 1e-8; public static readonly double sqrt2Pi = Math.Sqrt(2.0*Math.PI); @@ -28,5 +33,23 @@ namespace Assistance { p0 = new Point(p0.x + k*(bounds.y0 - p0.y), bounds.y0); } } + + public static Point noTransform(Point point) { + return point; + } + + public static Point transform(List funcs, Point point) { + Point p = point; + foreach(TransformFunc func in funcs) + p = func(p); + return p; + } + + public static Point splinePoint(Point p0, Point p1, Point t0, Point t1, double l) { + return p0*(( 2.0*l - 3.0)*l*l + 1.0) + + p1*((-2.0*l + 3.0)*l*l ) + + t0*(( l - 2.0)*l*l + l ) + + t1*(( l - 1.0)*l*l ); + } } } diff --git a/mono/Assistance/Guideline.cs b/mono/Assistance/Guideline.cs index 4f731b6..95ecc37 100644 --- a/mono/Assistance/Guideline.cs +++ b/mono/Assistance/Guideline.cs @@ -47,13 +47,6 @@ namespace Assistance { return sumDeviation/sumWeight; } - public Track modifyTrack(Track track) { - Track t = new Track(); - foreach(Point p in track.points) - t.points.Add( transformPoint(p) ); - return t; - } - public static Guideline findBest(List guidelines, Track track) { double bestWeight = double.PositiveInfinity; Guideline best = null; diff --git a/mono/Assistance/Track.cs b/mono/Assistance/Track.cs index 653ebf8..47131f7 100644 --- a/mono/Assistance/Track.cs +++ b/mono/Assistance/Track.cs @@ -10,6 +10,34 @@ namespace Assistance { public readonly List points = new List(); + private readonly List parents = new List(); + private readonly List transformFuncs = new List(); + + 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, double precision){ + 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(); @@ -18,6 +46,49 @@ namespace Assistance { bounds = bounds.expand(p); return bounds.inflate(Math.Max(pen.Width, penPreview.Width) + 2.0); } + + 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 = Geometry.transform(transformFuncs, 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) diff --git a/mono/Assistance/Workarea.cs b/mono/Assistance/Workarea.cs index 317bdd4..65d79c2 100644 --- a/mono/Assistance/Workarea.cs +++ b/mono/Assistance/Workarea.cs @@ -37,10 +37,10 @@ namespace Assistance { Guideline guideline = Guideline.findBest(guidelines, track); if (guideline != null) { track.draw(g, true); - guideline.modifyTrack(track).draw(g); + track.createChildAndBuild(guideline.transformPoint).draw(g); guideline.draw(g, true); } else { - track.draw(g); + track.createChildAndBuild(Geometry.noTransform).draw(g); } } else { getGuidelines(guidelines, target); @@ -65,9 +65,9 @@ namespace Assistance { getGuidelines(guidelines, track.points[0]); Guideline guideline = Guideline.findBest(guidelines, track); if (guideline == null) - return track; + return track.createChildAndBuild(Geometry.noTransform); - return guideline.modifyTrack(track); + return track.createChildAndBuild(guideline.transformPoint); } public void paintTrack(Track track) {