diff --git a/.gitignore b/.gitignore index c4a79590..11e726de 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ obj # built application files *.apk *.ap_ +.vs # nuget packages packages diff --git a/FreePIE.Core/FreePIE.Core.csproj b/FreePIE.Core/FreePIE.Core.csproj index 43dfaa53..446cd6a5 100644 --- a/FreePIE.Core/FreePIE.Core.csproj +++ b/FreePIE.Core/FreePIE.Core.csproj @@ -118,6 +118,7 @@ + diff --git a/FreePIE.Core/Model/Curve.cs b/FreePIE.Core/Model/Curve.cs index 9e9a59be..c8253b0d 100644 --- a/FreePIE.Core/Model/Curve.cs +++ b/FreePIE.Core/Model/Curve.cs @@ -1,22 +1,25 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace FreePIE.Core.Model { + public class Curve { - public Curve(List points) : this(null, points) {} + public Curve(IEnumerable points) : this(null, points) {} - public Curve(string name, List points) + public Curve(string name, IEnumerable points) { Name = name; - Points = points; + Points = points.ToList(); ValidateCurve = true; } public Curve() {} + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public List Points { get; set; } public string Name { get; set; } public bool? ValidateCurve { get; set; } @@ -43,8 +46,14 @@ private static List CalculateDefault(double yAxisMinValue, double yAxisMa .Select(value => new Point(value, value)) .ToList(); } - } + public override string ToString() + { + return "[" + string.Join(", ", Points.Select(p => $"({p.X}, {p.Y})")) + "]"; + } + + } + [DebuggerDisplay("({X}, {Y})")] public struct Point { public Point(double x, double y) : this() @@ -84,5 +93,26 @@ public bool Equals(Point other) { return X.Equals(other.X) && Y.Equals(other.Y); } + + public void Deconstruct(out double x, out double y) + { + x = this.X; + y = this.Y; + } + + public static implicit operator Point((double x, double y) tuple) + { + return new Point(tuple.x, tuple.y); + } + + public static implicit operator (double x, double y)(Point point) + { + return (point.X, point.Y); + } } + + + } + + diff --git a/FreePIE.Core/ScriptEngine/Globals/CurveGlobalProvider.cs b/FreePIE.Core/ScriptEngine/Globals/CurveGlobalProvider.cs index d54c03b6..a7b5fa88 100644 --- a/FreePIE.Core/ScriptEngine/Globals/CurveGlobalProvider.cs +++ b/FreePIE.Core/ScriptEngine/Globals/CurveGlobalProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using FreePIE.Core.Common; using FreePIE.Core.Contracts; @@ -7,6 +8,7 @@ namespace FreePIE.Core.ScriptEngine.Globals { + public class CurveGlobalProvider : IGlobalProvider { private readonly ISettingsManager settingsManager; @@ -20,6 +22,7 @@ public IEnumerable ListGlobals() return settingsManager.Settings.Curves.Where(c => !string.IsNullOrEmpty(c.Name)).Select(c => new CurveGlobal(c)); } + [DebuggerDisplay("{curve.Points}")] public class CurveGlobal : IGlobalNameProvider { private readonly Curve curve; diff --git a/FreePIE.Core/ScriptEngine/Globals/ScriptHelpers/CurveHelper.cs b/FreePIE.Core/ScriptEngine/Globals/ScriptHelpers/CurveHelper.cs new file mode 100644 index 00000000..83ee1841 --- /dev/null +++ b/FreePIE.Core/ScriptEngine/Globals/ScriptHelpers/CurveHelper.cs @@ -0,0 +1,43 @@ +using FreePIE.Core.Contracts; +using FreePIE.Core.Model; + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace FreePIE.Core.ScriptEngine.Globals.ScriptHelpers +{ + [Global(Name = "curves")] + public class CurveHelper : IScriptHelper + { + public CurveHelper() + { + + } + + /// + /// Create a curve from a list of points + /// + /// list of points in the format x,y,x,y,x,y,x,y... + /// a curve global + public CurveGlobalProvider.CurveGlobal create(double minimum, double maximum, params double[] points) + { + + var pointz = new List() { new Point(minimum, minimum) }; + + // ensure that all of the points values are between the minimum and maximum + + if (points.Any(p => p < minimum || p > maximum)) + throw new Exception("All points must be between the minimum and maximum values"); + + + pointz.AddRange(points.Select((x, i) => new { x, i }).GroupBy(p => p.i / 2).Select(g => new Point(g.First().x, g.Last().x))); + + pointz.Add(new Point(maximum, maximum)); + + return new CurveGlobalProvider.CurveGlobal(new Curve(Guid.NewGuid().ToString(), pointz) { ValidateCurve = true }); + } + + } + +} diff --git a/FreePIE.GUI/Events/SaveSettingsEvent.cs b/FreePIE.GUI/Events/SaveSettingsEvent.cs new file mode 100644 index 00000000..15bbedf3 --- /dev/null +++ b/FreePIE.GUI/Events/SaveSettingsEvent.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FreePIE.GUI.Events +{ + internal class SaveSettingsEvent + { + public SaveSettingsEvent() + { + + } + } +} diff --git a/FreePIE.GUI/FreePIE.GUI.csproj b/FreePIE.GUI/FreePIE.GUI.csproj index 61ca43aa..33ddaf5e 100644 --- a/FreePIE.GUI/FreePIE.GUI.csproj +++ b/FreePIE.GUI/FreePIE.GUI.csproj @@ -133,6 +133,7 @@ + diff --git a/FreePIE.GUI/Shells/Curves/CurveSettingsView.xaml b/FreePIE.GUI/Shells/Curves/CurveSettingsView.xaml index daaa7023..2b826ce4 100644 --- a/FreePIE.GUI/Shells/Curves/CurveSettingsView.xaml +++ b/FreePIE.GUI/Shells/Curves/CurveSettingsView.xaml @@ -1,19 +1,41 @@  - + - + + + + + + + - + + + + + diff --git a/FreePIE.GUI/Shells/Curves/CurveSettingsViewModel.cs b/FreePIE.GUI/Shells/Curves/CurveSettingsViewModel.cs index da49112c..ad659817 100644 --- a/FreePIE.GUI/Shells/Curves/CurveSettingsViewModel.cs +++ b/FreePIE.GUI/Shells/Curves/CurveSettingsViewModel.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using Caliburn.Micro; + +using FreePIE.Core.Common.Events; using FreePIE.Core.Persistence; using FreePIE.GUI.Events; using FreePIE.GUI.Result; @@ -14,11 +16,14 @@ public class CurveSettingsViewModel : ShellPresentationModel, Core.Common.Events { private readonly ISettingsManager settingsManager; private readonly Func curveModelFactory; + private readonly IEventAggregator eventAggregator; public CurveSettingsViewModel(IResultFactory resultFactory, ISettingsManager settingsManager, Func curveModelFactory, IEventAggregator eventAggregator) : base(resultFactory) { this.settingsManager = settingsManager; this.curveModelFactory = curveModelFactory; + this.eventAggregator = eventAggregator; + DisplayName = "Curve settings"; CreateCurvesModel(); eventAggregator.Subscribe(this); @@ -57,5 +62,10 @@ public BindableCollection Curves NotifyOfPropertyChange(() => Curves); } } + + public void Save() + { + eventAggregator.Publish(new SaveSettingsEvent()); + } } } diff --git a/FreePIE.GUI/Shells/MainShellViewModel.cs b/FreePIE.GUI/Shells/MainShellViewModel.cs index ae173480..c9758196 100644 --- a/FreePIE.GUI/Shells/MainShellViewModel.cs +++ b/FreePIE.GUI/Shells/MainShellViewModel.cs @@ -23,7 +23,7 @@ namespace FreePIE.GUI.Shells { public class MainShellViewModel : ShellPresentationModel, Core.Common.Events.IHandle - ,Core.Common.Events.IHandle + ,Core.Common.Events.IHandle, Core.Common.Events.IHandle { private const string dockingConfig = "layout.config"; private readonly IEventAggregator eventAggregator; @@ -220,5 +220,10 @@ public void Handle(ExitingEvent message) var layoutSerializer = new XmlLayoutSerializer(DockingManager); layoutSerializer.Serialize(paths.GetDataPath(dockingConfig)); } + + void Core.Common.Events.IHandle.Handle(SaveSettingsEvent message) + { + this.settingsManager.Save(); + } } } diff --git a/FreePIE.GUI/Views/Curves/CurveView.xaml b/FreePIE.GUI/Views/Curves/CurveView.xaml index d9f0cb7d..84d095aa 100644 --- a/FreePIE.GUI/Views/Curves/CurveView.xaml +++ b/FreePIE.GUI/Views/Curves/CurveView.xaml @@ -6,6 +6,7 @@ xmlns:Charts="clr-namespace:Visiblox.Charts;assembly=Visiblox.Charts" xmlns:Visiblox="clr-namespace:FreePIE.GUI.Common.Visiblox" xmlns:Curves="clr-namespace:FreePIE.GUI.Views.Curves"> + @@ -14,9 +15,9 @@ Validate Curve - + - + @@ -47,18 +48,28 @@ - + - - - - - - - - + + + + + + + + + + + + - + + diff --git a/FreePIE.GUI/Views/Curves/CurveViewModel.cs b/FreePIE.GUI/Views/Curves/CurveViewModel.cs index c1aafebd..40a1f273 100644 --- a/FreePIE.GUI/Views/Curves/CurveViewModel.cs +++ b/FreePIE.GUI/Views/Curves/CurveViewModel.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using Caliburn.Micro; +using Caliburn.Micro; + using FreePIE.Core.Common; using FreePIE.Core.Common.Extensions; using FreePIE.Core.Model; @@ -11,9 +8,16 @@ using FreePIE.GUI.Events; using FreePIE.GUI.Result; using FreePIE.GUI.Shells.Curves; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; + using IEventAggregator = FreePIE.Core.Common.Events.IEventAggregator; using Point = FreePIE.Core.Model.Point; + namespace FreePIE.GUI.Views.Curves { public class CurveViewModel : PropertyChangedBase @@ -78,6 +82,34 @@ public IEnumerable Delete() eventAggregator.Publish(new DeleteCurveEvent(this)); } + + /// + /// Generates a python script that creates the curve + /// + /// + public void Script() + { + + string python = $"{Curve.Name} = curves.create({Curve.Points.First().X}, {Curve.Points.Last().X}"; + foreach (var point in Curve.Points.Skip(1).TakeAllButLast()) + { + python += $", {point.X:0.000}, {point.Y:0.000}"; + } + python += ")"; + + + try + { + Clipboard.SetDataObject(python); + } + catch (Exception x) + { + + } + + } + + public IEnumerable Reset() { var dialog = resultFactory.ShowDialog().Configure(m => m.Init(Curve)); @@ -104,6 +136,11 @@ public bool HasSelectedPoint get { return selectedPointIndex.HasValue; } } + public bool HasPoints + { + get { return Curve.Points.Any(); } + } + private bool canSetDefault; public bool CanSetDefault { @@ -242,7 +279,6 @@ public IEnumerable SelectablePoints selectablePoints = value; NotifyOfPropertyChange(() => SelectablePoints); } - } - + } } } diff --git a/FreePIE.Tests.Core/CurveHelperTests.cs b/FreePIE.Tests.Core/CurveHelperTests.cs new file mode 100644 index 00000000..a5d6b92a --- /dev/null +++ b/FreePIE.Tests.Core/CurveHelperTests.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using FreePIE.Core.Common.Extensions; +using FreePIE.Core.ScriptEngine.Globals; +using FreePIE.Core.ScriptEngine.Globals.ScriptHelpers; +using FreePIE.Tests.Test; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace FreePIE.Tests.Core +{ + [TestClass] + public class CurveHelperTests : TestBase + { + protected CurveHelper curveHelper; + + public CurveHelperTests() + { + curveHelper = Get(); + } + + [TestMethod] + public void It_should_return_a_named_CurveGlobal() + { + var curve = curveHelper.create(0,1); + Assert.IsInstanceOfType(curve, typeof(CurveGlobalProvider.CurveGlobal)); + Assert.IsTrue(curve.Name.Length == 36); + } + + [TestMethod] + public void It_should_solve_for_y() + { + var curve = curveHelper.create(0,1); + + var y = curve.getY(0.5); + + Assert.AreEqual(0.5, y); + } + + [TestMethod] + public void It_should_ease_in_should_be_correct() + { + double min = 0; + double max = 100; + + CurveGlobalProvider.CurveGlobal curve = curveHelper.create(min, max,60,12,93,70); + + var results = new Dictionary(); + for(var i = min; i <= max; i+= (int)(max/10)) + { + results.Add(i , Math.Round(curve.getY(i),2)); + } + + Assert.IsTrue(results.All(_ => _.Value >= min && _.Value <= max)); + + Assert.AreEqual(results.First().Key , results.First().Value); + Assert.AreEqual(results.Last().Key , results.Last().Value); + + Assert.IsTrue(results.Skip(1).TakeAllButLast().All(_ => _.Value < _.Key)); + + + + } + + [TestMethod] + public void It_should_ease_out_should_be_correct() + { + double min = 0; + double max = 100; + + CurveGlobalProvider.CurveGlobal curve = curveHelper.create(min, max, 12, 40, 50, 91); + + var results = new Dictionary(); + for (var i = min; i <= max; i += (int)(max / 10)) + { + results.Add(i, Math.Round(curve.getY(i), 2)); + } + + Assert.IsTrue(results.All(_ => _.Value >= min && _.Value <= max)); + + Assert.AreEqual(results.First().Key, results.First().Value); + Assert.AreEqual(results.Last().Key, results.Last().Value); + + Assert.IsTrue(results.Skip(1).TakeAllButLast().All(_ => _.Value > _.Key)); + + + + } + } +} diff --git a/FreePIE.Tests.Core/FreePIE.Tests.Core.csproj b/FreePIE.Tests.Core/FreePIE.Tests.Core.csproj index efaf6ff0..b57ed58a 100644 --- a/FreePIE.Tests.Core/FreePIE.Tests.Core.csproj +++ b/FreePIE.Tests.Core/FreePIE.Tests.Core.csproj @@ -55,6 +55,7 @@ +