summaryrefslogtreecommitdiff
path: root/grapher/Models
diff options
context:
space:
mode:
authora1xd <[email protected]>2020-08-14 03:48:40 -0400
committerGitHub <[email protected]>2020-08-14 03:48:40 -0400
commit0621a7ebd431102d720497a143190505dcfeb7a1 (patch)
tree01d7df8f55e5a1cce90617fd876eaf994eb26846 /grapher/Models
parentMerge pull request #14 from JacobPalecki/GainCap (diff)
parentFix initial points, add poll time constant (diff)
downloadrawaccel-0621a7ebd431102d720497a143190505dcfeb7a1.tar.xz
rawaccel-0621a7ebd431102d720497a143190505dcfeb7a1.zip
Merge pull request #15 from JacobPalecki/GUI
GUI: Add x/y graphs, moving dot
Diffstat (limited to 'grapher/Models')
-rw-r--r--grapher/Models/AccelGUI.cs101
-rw-r--r--grapher/Models/Calculations/AccelCalculator.cs145
-rw-r--r--grapher/Models/Calculations/AccelChartData.cs61
-rw-r--r--grapher/Models/Calculations/AccelData.cs74
-rw-r--r--grapher/Models/Charts/AccelCharts.cs203
-rw-r--r--grapher/Models/Charts/ChartXY.cs213
-rw-r--r--grapher/Models/Charts/EstimatedPoints.cs25
-rw-r--r--grapher/Models/Fields/Field.cs226
-rw-r--r--grapher/Models/Fields/FieldXY.cs135
-rw-r--r--grapher/Models/Mouse/MouseWatcher.cs727
-rw-r--r--grapher/Models/Mouse/PointData.cs41
-rw-r--r--grapher/Models/Options/AccelOptions.cs79
-rw-r--r--grapher/Models/Options/CapOptions.cs137
-rw-r--r--grapher/Models/Options/Option.cs58
-rw-r--r--grapher/Models/Options/OptionXY.cs83
15 files changed, 2308 insertions, 0 deletions
diff --git a/grapher/Models/AccelGUI.cs b/grapher/Models/AccelGUI.cs
new file mode 100644
index 0000000..8eb2226
--- /dev/null
+++ b/grapher/Models/AccelGUI.cs
@@ -0,0 +1,101 @@
+using grapher.Models.Calculations;
+using grapher.Models.Mouse;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+
+namespace grapher
+{
+ public class AccelGUI
+ {
+
+ #region constructors
+
+ public AccelGUI(
+ RawAcceleration accelForm,
+ AccelCharts accelCharts,
+ ManagedAccel managedAccel,
+ AccelOptions accelOptions,
+ OptionXY sensitivity,
+ Option rotation,
+ OptionXY weight,
+ CapOptions cap,
+ Option offset,
+ Option acceleration,
+ Option limtOrExp,
+ Option midpoint,
+ Button writeButton,
+ Label mouseMoveLabel)
+ {
+ AccelForm = accelForm;
+ AccelCharts = accelCharts;
+ ManagedAcceleration = managedAccel;
+ AccelerationOptions = accelOptions;
+ Sensitivity = sensitivity;
+ Rotation = rotation;
+ Weight = weight;
+ Cap = cap;
+ Offset = offset;
+ Acceleration = acceleration;
+ LimitOrExponent = limtOrExp;
+ Midpoint = midpoint;
+ WriteButton = writeButton;
+
+ ManagedAcceleration.ReadFromDriver();
+ UpdateGraph();
+
+ MouseWatcher = new MouseWatcher(AccelForm, mouseMoveLabel, AccelCharts);
+ }
+
+ #endregion constructors
+
+ #region properties
+
+ public RawAcceleration AccelForm { get; }
+
+ public AccelCharts AccelCharts { get; }
+
+ public ManagedAccel ManagedAcceleration { get; }
+
+ public AccelOptions AccelerationOptions { get; }
+
+ public OptionXY Sensitivity { get; }
+
+ public Option Rotation { get; }
+
+ public OptionXY Weight { get; }
+
+ public CapOptions Cap { get; }
+
+ public Option Offset { get; }
+
+ public Option Acceleration { get; }
+
+ public Option LimitOrExponent { get; }
+
+ public Option Midpoint { get; }
+
+ public Button WriteButton { get; }
+
+ public MouseWatcher MouseWatcher { get; }
+
+ #endregion properties
+
+ #region methods
+
+
+ public void UpdateGraph()
+ {
+ AccelCalculator.Calculate(AccelCharts.AccelData, ManagedAcceleration);
+ AccelCharts.Bind();
+ }
+
+ #endregion methods
+ }
+
+}
diff --git a/grapher/Models/Calculations/AccelCalculator.cs b/grapher/Models/Calculations/AccelCalculator.cs
new file mode 100644
index 0000000..bba3c32
--- /dev/null
+++ b/grapher/Models/Calculations/AccelCalculator.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace grapher.Models.Calculations
+{
+ public static class AccelCalculator
+ {
+ public const int MaxCombined = 100;
+ public const int MaxXY = 150;
+
+ public struct MagnitudeData
+ {
+ public double magnitude;
+ public int x;
+ public int y;
+ }
+
+ public static ReadOnlyCollection<MagnitudeData> MagnitudesCombined = GetMagnitudes();
+ public static ReadOnlyCollection<MagnitudeData> MagnitudesX = GetMagnitudesX();
+ public static ReadOnlyCollection<MagnitudeData> MagnitudesY = GetMagnitudesY();
+
+ public static void Calculate(AccelData data, ManagedAccel accel)
+ {
+ data.Clear();
+
+ Calculate(data.Combined, accel, accel.GetSensitivityX(), MagnitudesCombined);
+ Calculate(data.X, accel, accel.GetSensitivityX(), MagnitudesX);
+ Calculate(data.Y, accel, accel.GetSensitivityY(), MagnitudesY);
+ }
+
+ public static void Calculate(AccelChartData data, ManagedAccel accel, double starter, ICollection<MagnitudeData> magnitudeData)
+ {
+ double lastInputMagnitude = 0;
+ double lastOutputMagnitude = 0;
+
+ foreach (var magnitudeDatum in magnitudeData)
+ {
+ var output = accel.Accelerate(magnitudeDatum.x, magnitudeDatum.y, 1);
+
+ var outMagnitude = Magnitude(output.Item1, output.Item2);
+ var ratio = magnitudeDatum.magnitude > 0 ? outMagnitude / magnitudeDatum.magnitude : starter;
+
+ var inDiff = magnitudeDatum.magnitude - lastInputMagnitude;
+ var outDiff = outMagnitude - lastOutputMagnitude;
+ var slope = inDiff > 0 ? outDiff / inDiff : starter;
+
+ if (!data.AccelPoints.ContainsKey(magnitudeDatum.magnitude))
+ {
+ data.AccelPoints.Add(magnitudeDatum.magnitude, ratio);
+ }
+
+ if (!data.VelocityPoints.ContainsKey(magnitudeDatum.magnitude))
+ {
+ data.VelocityPoints.Add(magnitudeDatum.magnitude, outMagnitude);
+ }
+
+ if (!data.GainPoints.ContainsKey(magnitudeDatum.magnitude))
+ {
+ data.GainPoints.Add(magnitudeDatum.magnitude, slope);
+ }
+
+ lastInputMagnitude = magnitudeDatum.magnitude;
+ lastOutputMagnitude = outMagnitude;
+ }
+
+ data.OrderedVelocityPointsList.AddRange(data.VelocityPoints.Values.ToList());
+ }
+
+ public static ReadOnlyCollection<MagnitudeData> GetMagnitudes()
+ {
+ var magnitudes = new List<MagnitudeData>();
+ for (int i = 0; i < MaxCombined; i++)
+ {
+ for (int j = 0; j <= i; j++)
+ {
+ MagnitudeData magnitudeData;
+ magnitudeData.magnitude = Magnitude(i, j);
+ magnitudeData.x = i;
+ magnitudeData.y = j;
+ magnitudes.Add(magnitudeData);
+ }
+ }
+
+ magnitudes.Sort((m1, m2) => m1.magnitude.CompareTo(m2.magnitude));
+
+ return magnitudes.AsReadOnly();
+ }
+
+ public static ReadOnlyCollection<MagnitudeData> GetMagnitudesX()
+ {
+ var magnitudes = new List<MagnitudeData>();
+
+ for (int i = 0; i < MaxXY; i++)
+ {
+ MagnitudeData magnitudeData;
+ magnitudeData.magnitude = i;
+ magnitudeData.x = i;
+ magnitudeData.y = 0;
+ magnitudes.Add(magnitudeData);
+ }
+
+ return magnitudes.AsReadOnly();
+ }
+
+ public static ReadOnlyCollection<MagnitudeData> GetMagnitudesY()
+ {
+ var magnitudes = new List<MagnitudeData>();
+
+ for (int i = 0; i < MaxXY; i++)
+ {
+ MagnitudeData magnitudeData;
+ magnitudeData.magnitude = i;
+ magnitudeData.x = 0;
+ magnitudeData.y = i;
+ magnitudes.Add(magnitudeData);
+ }
+
+ return magnitudes.AsReadOnly();
+ }
+
+ public static double Magnitude(int x, int y)
+ {
+ return Math.Sqrt(x * x + y * y);
+ }
+
+ public static double Magnitude(double x, double y)
+ {
+ return Math.Sqrt(x * x + y * y);
+ }
+
+ public static double Velocity(int x, int y, double time)
+ {
+ return Magnitude(x, y) / time;
+ }
+
+ public static double Velocity(double x, double y, double time)
+ {
+ return Magnitude(x, y) / time;
+ }
+ }
+}
diff --git a/grapher/Models/Calculations/AccelChartData.cs b/grapher/Models/Calculations/AccelChartData.cs
new file mode 100644
index 0000000..20142a7
--- /dev/null
+++ b/grapher/Models/Calculations/AccelChartData.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace grapher.Models.Calculations
+{
+ public class AccelChartData
+ {
+ public AccelChartData()
+ {
+ AccelPoints = new SortedDictionary<double, double>();
+ VelocityPoints = new SortedDictionary<double, double>();
+ GainPoints = new SortedDictionary<double, double>();
+ OrderedVelocityPointsList = new List<double>();
+ OutVelocityToPoints = new Dictionary<double, (double, double, double)>();
+ }
+
+ public SortedDictionary<double, double> AccelPoints { get; }
+
+ public SortedDictionary<double, double> VelocityPoints { get; }
+
+ public SortedDictionary<double, double> GainPoints { get; }
+
+ public List<double> OrderedVelocityPointsList { get; }
+
+ public Dictionary<double, (double, double, double)> OutVelocityToPoints { get; }
+
+ public void Clear()
+ {
+ AccelPoints.Clear();
+ VelocityPoints.Clear();
+ GainPoints.Clear();
+ OrderedVelocityPointsList.Clear();
+ OutVelocityToPoints.Clear();
+ }
+
+ public (double, double, double) FindPointValuesFromOut(double outVelocityValue)
+ {
+ if (OutVelocityToPoints.TryGetValue(outVelocityValue, out var values))
+ {
+ return values;
+ }
+ else
+ {
+ var velIdx = OrderedVelocityPointsList.BinarySearch(outVelocityValue);
+
+ if (velIdx < 0)
+ {
+ velIdx = ~velIdx;
+ }
+
+ velIdx = Math.Min(velIdx, VelocityPoints.Count - 1);
+ values = (VelocityPoints.ElementAt(velIdx).Key, AccelPoints.ElementAt(velIdx).Value, GainPoints.ElementAt(velIdx).Value);
+ OutVelocityToPoints.Add(outVelocityValue, values);
+ return values;
+ }
+ }
+ }
+}
diff --git a/grapher/Models/Calculations/AccelData.cs b/grapher/Models/Calculations/AccelData.cs
new file mode 100644
index 0000000..683c67e
--- /dev/null
+++ b/grapher/Models/Calculations/AccelData.cs
@@ -0,0 +1,74 @@
+using grapher.Models.Charts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static grapher.AccelCharts;
+
+namespace grapher.Models.Calculations
+{
+ public class AccelData
+ {
+
+ public AccelData(
+ EstimatedPoints combined,
+ EstimatedPoints x,
+ EstimatedPoints y)
+ {
+ Combined = new AccelChartData();
+ X = new AccelChartData();
+ Y = new AccelChartData();
+
+ Estimated = combined;
+ EstimatedX = x;
+ EstimatedY = y;
+ }
+
+ public AccelChartData Combined { get; }
+
+ public AccelChartData X { get; }
+
+ public AccelChartData Y { get; }
+
+ private EstimatedPoints Estimated { get; }
+
+ private EstimatedPoints EstimatedX { get; }
+
+ private EstimatedPoints EstimatedY { get; }
+
+ public void Clear()
+ {
+ Combined.Clear();
+ X.Clear();
+ Y.Clear();
+ }
+
+ public void CalculateDots(int x, int y, double timeInMs)
+ {
+ var magnitude = AccelCalculator.Velocity(x, y, timeInMs);
+
+ (var inCombVel, var combSens, var combGain) = Combined.FindPointValuesFromOut(magnitude);
+ Estimated.Velocity.Set(inCombVel, magnitude);
+ Estimated.Sensitivity.Set(inCombVel, combSens);
+ Estimated.Gain.Set(inCombVel, combGain);
+ }
+
+ public void CalculateDotsXY(int x, int y, double timeInMs)
+ {
+ var outX = Math.Abs(x);
+ var outY = Math.Abs(y);
+
+ (var inXVelocity, var xSensitivity, var xGain) = X.FindPointValuesFromOut(outX);
+ EstimatedX.Velocity.Set(inXVelocity, outX);
+ EstimatedX.Sensitivity.Set(inXVelocity, xSensitivity);
+ EstimatedX.Gain.Set(inXVelocity, xGain);
+
+ (var inYVelocity, var ySensitivity, var yGain) = Y.FindPointValuesFromOut(outY);
+ EstimatedY.Velocity.Set(inYVelocity, outY);
+ EstimatedY.Sensitivity.Set(inYVelocity, ySensitivity);
+ EstimatedY.Gain.Set(inYVelocity, yGain);
+ }
+
+ }
+}
diff --git a/grapher/Models/Charts/AccelCharts.cs b/grapher/Models/Charts/AccelCharts.cs
new file mode 100644
index 0000000..1aa3909
--- /dev/null
+++ b/grapher/Models/Charts/AccelCharts.cs
@@ -0,0 +1,203 @@
+using grapher.Models.Calculations;
+using grapher.Models.Charts;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Security.Permissions;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+
+namespace grapher
+{
+ public class AccelCharts
+ {
+ public const int ChartSeparationVertical = 10;
+
+ /// <summary> Needed to show full contents in form. Unsure why. </summary>
+ public const int FormHeightPadding = 35;
+
+ public AccelCharts(
+ Form form,
+ ChartXY sensitivityChart,
+ ChartXY velocityChart,
+ ChartXY gainChart,
+ ToolStripMenuItem enableVelocityAndGain,
+ ICollection<CheckBox> checkBoxesXY)
+ {
+ Estimated = new EstimatedPoints();
+ EstimatedX = new EstimatedPoints();
+ EstimatedY = new EstimatedPoints();
+ AccelData = new AccelData(Estimated, EstimatedX, EstimatedY);
+
+ ContaingForm = form;
+ SensitivityChart = sensitivityChart;
+ VelocityChart = velocityChart;
+ GainChart = gainChart;
+ EnableVelocityAndGain = enableVelocityAndGain;
+ CheckBoxesXY = checkBoxesXY;
+
+ SensitivityChart.SetPointBinds(Estimated.Sensitivity, EstimatedX.Sensitivity, EstimatedY.Sensitivity);
+ VelocityChart.SetPointBinds(Estimated.Velocity, EstimatedX.Velocity, EstimatedY.Velocity);
+ GainChart.SetPointBinds(Estimated.Gain, EstimatedX.Gain, EstimatedY.Gain);
+
+ SensitivityChart.SetTop(0);
+ VelocityChart.SetHeight(SensitivityChart.Height);
+ VelocityChart.SetTop(SensitivityChart.Height + ChartSeparationVertical);
+ GainChart.SetHeight(SensitivityChart.Height);
+ GainChart.SetTop(VelocityChart.Top + VelocityChart.Height + ChartSeparationVertical);
+
+ Rectangle screenRectangle = ContaingForm.RectangleToScreen(ContaingForm.ClientRectangle);
+ FormBorderHeight = screenRectangle.Top - ContaingForm.Top;
+
+ EnableVelocityAndGain.Click += new System.EventHandler(OnEnableClick);
+ EnableVelocityAndGain.CheckedChanged += new System.EventHandler(OnEnableCheckStateChange);
+
+ HideVelocityAndGain();
+ Combined = false;
+ ShowCombined();
+ }
+
+ public Form ContaingForm { get; }
+
+ public ChartXY SensitivityChart { get; }
+
+ public ChartXY VelocityChart { get; }
+
+ public ChartXY GainChart { get; }
+
+ public ToolStripMenuItem EnableVelocityAndGain { get; }
+
+ public AccelData AccelData { get; }
+
+ private EstimatedPoints Estimated { get; }
+
+ private EstimatedPoints EstimatedX { get; }
+
+ private EstimatedPoints EstimatedY { get; }
+
+ private ICollection<CheckBox> CheckBoxesXY { get; }
+
+ private bool Combined { get; set; }
+
+ private int FormBorderHeight { get; }
+
+ public void MakeDots(int x, int y, double timeInMs)
+ {
+ if (Combined)
+ {
+ AccelData.CalculateDots(x, y, timeInMs);
+ }
+ else
+ {
+ AccelData.CalculateDotsXY(x, y, timeInMs);
+ }
+ }
+
+ public void DrawPoints()
+ {
+ SensitivityChart.DrawPoints();
+ VelocityChart.DrawPoints();
+ GainChart.DrawPoints();
+ }
+
+ public void Bind()
+ {
+ if (Combined)
+ {
+ SensitivityChart.Bind(AccelData.Combined.AccelPoints);
+ VelocityChart.Bind(AccelData.Combined.VelocityPoints);
+ GainChart.Bind(AccelData.Combined.GainPoints);
+ }
+ else
+ {
+ SensitivityChart.BindXY(AccelData.X.AccelPoints, AccelData.Y.AccelPoints);
+ VelocityChart.BindXY(AccelData.X.VelocityPoints, AccelData.Y.VelocityPoints);
+ GainChart.BindXY(AccelData.X.GainPoints, AccelData.Y.GainPoints);
+ }
+ }
+
+ public void RefreshXY()
+ {
+ if (CheckBoxesXY.All(box => box.Checked))
+ {
+ ShowCombined();
+ }
+ else
+ {
+ ShowXandYSeparate();
+ }
+ }
+
+ private void OnEnableClick(object sender, EventArgs e)
+ {
+ EnableVelocityAndGain.Checked = !EnableVelocityAndGain.Checked;
+ }
+
+ private void OnEnableCheckStateChange(object sender, EventArgs e)
+ {
+ if (EnableVelocityAndGain.Checked)
+ {
+ ShowVelocityAndGain();
+ }
+ else
+ {
+ HideVelocityAndGain();
+ }
+ }
+
+ private void ShowVelocityAndGain()
+ {
+ VelocityChart.Show();
+ GainChart.Show();
+ ContaingForm.Height = SensitivityChart.Height +
+ ChartSeparationVertical +
+ VelocityChart.Height +
+ ChartSeparationVertical +
+ GainChart.Height +
+ FormBorderHeight;
+ }
+
+ private void HideVelocityAndGain()
+ {
+ VelocityChart.Hide();
+ GainChart.Hide();
+ ContaingForm.Height = SensitivityChart.Height + FormBorderHeight;
+ }
+
+ private void ShowXandYSeparate()
+ {
+ if (Combined)
+ {
+ Combined = false;
+
+ SensitivityChart.SetSeparate();
+ VelocityChart.SetSeparate();
+ GainChart.SetSeparate();
+ UpdateFormWidth();
+ Bind();
+ }
+ }
+
+ private void ShowCombined()
+ {
+ if (!Combined)
+ {
+ Combined = true;
+
+ SensitivityChart.SetCombined();
+ VelocityChart.SetCombined();
+ GainChart.SetCombined();
+ UpdateFormWidth();
+ Bind();
+ }
+ }
+
+ private void UpdateFormWidth()
+ {
+ ContaingForm.Width = SensitivityChart.Left + SensitivityChart.Width;
+ }
+ }
+}
diff --git a/grapher/Models/Charts/ChartXY.cs b/grapher/Models/Charts/ChartXY.cs
new file mode 100644
index 0000000..81874a2
--- /dev/null
+++ b/grapher/Models/Charts/ChartXY.cs
@@ -0,0 +1,213 @@
+using grapher.Models.Charts;
+using grapher.Models.Mouse;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Forms.DataVisualization.Charting;
+using static grapher.AccelCharts;
+
+namespace grapher
+{
+ public class ChartXY
+ {
+ #region Consts
+
+ public const int ChartSeparationHorizontal = 10;
+
+ #endregion Consts
+
+ #region Constructors
+
+ public ChartXY(Chart chartX, Chart chartY)
+ {
+ ChartX = chartX;
+ ChartY = chartY;
+
+ ChartY.Top = ChartX.Top;
+ ChartY.Height = ChartX.Height;
+ ChartY.Width = ChartX.Width;
+ ChartY.Left = ChartX.Left + ChartX.Width + ChartSeparationHorizontal;
+
+ SetupChart(ChartX);
+ SetupChart(ChartY);
+ }
+
+ #endregion Constructors
+
+ #region Properties
+
+ public Chart ChartX { get; }
+
+ public Chart ChartY { get; }
+
+ public int Height {
+ get
+ {
+ return ChartX.Height;
+ }
+ }
+
+ public int Width {
+ get
+ {
+ if (Combined)
+ {
+ return ChartX.Width;
+ }
+ else
+ {
+ return ChartY.Left + ChartY.Width - ChartX.Left;
+ }
+ }
+ }
+
+ public int Top {
+ get
+ {
+ return ChartX.Top;
+ }
+ }
+
+ public int Left {
+ get
+ {
+ return ChartX.Left;
+ }
+ }
+
+ public bool Combined { get; private set; }
+
+ private PointData CombinedPointData { get; set; }
+
+ private PointData XPointData { get; set; }
+
+ private PointData YPointData { get; set; }
+
+ #endregion Properties
+
+ #region Methods
+
+ public static void SetupChart(Chart chart)
+ {
+ chart.ChartAreas[0].AxisX.RoundAxisValues();
+
+ chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
+ chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
+
+ chart.ChartAreas[0].AxisY.ScaleView.MinSize = 0.01;
+ chart.ChartAreas[0].AxisY.ScaleView.SmallScrollSize = 0.001;
+
+ chart.ChartAreas[0].CursorY.Interval = 0.001;
+
+ chart.ChartAreas[0].CursorX.AutoScroll = true;
+ chart.ChartAreas[0].CursorY.AutoScroll = true;
+
+ chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
+ chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
+
+ chart.ChartAreas[0].CursorX.IsUserEnabled = true;
+ chart.ChartAreas[0].CursorY.IsUserEnabled = true;
+
+ chart.Series[1].Points.Clear();
+ chart.Series[1].Points.AddXY(0, 0);
+ }
+
+ public static void DrawPoint(Chart chart, PointData point)
+ {
+ if (chart.Visible)
+ {
+ point.Get(out var x, out var y);
+ chart.Series[1].Points.DataBindXY(x, y);
+ chart.Update();
+ }
+ }
+
+ public void SetPointBinds(PointData combined, PointData x, PointData y)
+ {
+ CombinedPointData = combined;
+ XPointData = x;
+ YPointData = y;
+ }
+
+ public void DrawPoints()
+ {
+ if(Combined)
+ {
+ DrawPoint(ChartX, CombinedPointData);
+ }
+ else
+ {
+ DrawPoint(ChartX, XPointData);
+ DrawPoint(ChartY, YPointData);
+ }
+ }
+
+ public void Bind(IDictionary data)
+ {
+ ChartX.Series[0].Points.DataBindXY(data.Keys, data.Values);
+ }
+
+ public void BindXY(IDictionary dataX, IDictionary dataY)
+ {
+ ChartX.Series[0].Points.DataBindXY(dataX.Keys, dataX.Values);
+ ChartY.Series[0].Points.DataBindXY(dataY.Keys, dataY.Values);
+ }
+
+ public void SetCombined()
+ {
+ if (!Combined)
+ {
+ ChartY.Hide();
+ Combined = true;
+ }
+ }
+
+ public void SetSeparate()
+ {
+ if (Combined)
+ {
+ if (ChartX.Visible)
+ {
+ ChartY.Show();
+ }
+
+ Combined = false;
+ }
+ }
+
+ public void Hide()
+ {
+ ChartX.Hide();
+ ChartY.Hide();
+ }
+
+ public void Show()
+ {
+ ChartX.Show();
+
+ if (!Combined)
+ {
+ ChartY.Show();
+ }
+ }
+
+ public void SetTop(int top)
+ {
+ ChartX.Top = top;
+ ChartY.Top = top;
+ }
+
+ public void SetHeight(int height)
+ {
+ ChartX.Height = height;
+ ChartY.Height = height;
+ }
+
+ #endregion Methods
+ }
+}
diff --git a/grapher/Models/Charts/EstimatedPoints.cs b/grapher/Models/Charts/EstimatedPoints.cs
new file mode 100644
index 0000000..fa0718b
--- /dev/null
+++ b/grapher/Models/Charts/EstimatedPoints.cs
@@ -0,0 +1,25 @@
+using grapher.Models.Mouse;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace grapher.Models.Charts
+{
+ public class EstimatedPoints
+ {
+ public EstimatedPoints()
+ {
+ Sensitivity = new PointData();
+ Velocity = new PointData();
+ Gain = new PointData();
+ }
+
+ public PointData Sensitivity { get; }
+
+ public PointData Velocity { get; }
+
+ public PointData Gain { get; }
+ }
+}
diff --git a/grapher/Models/Fields/Field.cs b/grapher/Models/Fields/Field.cs
new file mode 100644
index 0000000..8d75172
--- /dev/null
+++ b/grapher/Models/Fields/Field.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher
+{
+ public class Field
+ {
+ #region Constants
+
+ public const string DefaultFormatString = "0.#########";
+
+ #endregion Constants
+
+ #region Enums
+
+ public enum FieldState
+ {
+ Undefined,
+ Default,
+ Typing,
+ Entered,
+ Unavailable,
+ }
+
+ #endregion Enums
+
+
+ #region Constructors
+
+ public Field(TextBox box, Form containingForm, double defaultData)
+ {
+ DefaultText = DecimalString(defaultData);
+ Box = box;
+ Data = defaultData;
+ DefaultData = defaultData;
+ State = FieldState.Undefined;
+ ContainingForm = containingForm;
+ FormatString = DefaultFormatString;
+ box.KeyDown += new System.Windows.Forms.KeyEventHandler(KeyDown);
+ box.Leave += new System.EventHandler(FocusLeave);
+
+ SetToDefault();
+ }
+
+ #endregion Constructors
+
+ #region Properties
+
+ public TextBox Box { get; }
+
+ private Form ContainingForm { get; }
+
+ public double Data { get; private set; }
+
+ public string FormatString { get; set; }
+
+ public string DefaultText { get; }
+
+ public FieldState State { get; private set; }
+
+ public FieldState PreviousState { get; private set; }
+
+ private double DefaultData { get; }
+
+ #endregion Properties
+
+ #region Methods
+
+ public void SetToDefault()
+ {
+ if (State != FieldState.Default)
+ {
+ Box.BackColor = Color.White;
+ Box.ForeColor = Color.Gray;
+ State = FieldState.Default;
+ PreviousState = FieldState.Default;
+ }
+
+ Data = DefaultData;
+ Box.Text = DefaultText;
+ ContainingForm.ActiveControl = null;
+ }
+
+ public void SetToTyping()
+ {
+ if (State != FieldState.Typing)
+ {
+ Box.BackColor = Color.White;
+ Box.ForeColor = Color.Black;
+
+ PreviousState = State;
+ State = FieldState.Typing;
+ }
+
+ Box.Text = string.Empty;
+ }
+
+ public void SetToEntered()
+ {
+ if (State != FieldState.Entered)
+ {
+ Box.BackColor = Color.AntiqueWhite;
+ Box.ForeColor = Color.DarkGray;
+
+ PreviousState = State;
+ State = FieldState.Entered;
+ }
+
+ ContainingForm.ActiveControl = null;
+ }
+
+ public void SetToEntered(double value)
+ {
+ SetToEntered();
+
+ Data = value;
+ Box.Text = DecimalString(Data);
+ }
+
+ public void SetToUnavailable()
+ {
+ if (State != FieldState.Unavailable)
+ {
+ Box.BackColor = Color.LightGray;
+ Box.ForeColor = Color.LightGray;
+ Box.Text = string.Empty;
+
+ PreviousState = State;
+ State = FieldState.Unavailable;
+ }
+ }
+
+ public void KeyDown(object sender, KeyEventArgs e)
+ {
+ switch(State)
+ {
+ case FieldState.Default:
+ if (e.KeyCode == Keys.Enter)
+ {
+ SetToDefault();
+ }
+ else
+ {
+ SetToTyping();
+ }
+ break;
+
+ case FieldState.Entered:
+ if (e.KeyCode != Keys.Enter)
+ {
+ SetToTyping();
+ }
+ break;
+ case FieldState.Typing:
+ HandleTyping(sender, e);
+ break;
+ case FieldState.Unavailable:
+ Box.Text = string.Empty;
+ ContainingForm.ActiveControl = null;
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void FocusLeave(object sender, EventArgs e)
+ {
+ if (State == FieldState.Typing)
+ {
+ TextToData();
+ }
+ }
+
+ private void HandleTyping(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Enter)
+ {
+ TextToData();
+
+ e.Handled = true;
+ e.SuppressKeyPress = true;
+ }
+ else if (e.KeyCode == Keys.Escape)
+ {
+ ContainingForm.ActiveControl = null;
+ e.Handled = true;
+ e.SuppressKeyPress = true;
+ }
+ }
+
+ private void TextToData()
+ {
+ try
+ {
+ Data = Convert.ToDouble(Box.Text);
+ }
+ catch
+ {
+ }
+
+ Box.Text = DecimalString(Data);
+
+ if (string.Equals(Box.Text, DefaultText))
+ {
+ SetToDefault();
+ }
+ else
+ {
+ SetToEntered();
+ }
+ }
+
+ private string DecimalString(double value)
+ {
+ return value.ToString(FormatString);
+ }
+
+
+ #endregion Methods
+ }
+}
diff --git a/grapher/Models/Fields/FieldXY.cs b/grapher/Models/Fields/FieldXY.cs
new file mode 100644
index 0000000..609af9d
--- /dev/null
+++ b/grapher/Models/Fields/FieldXY.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher
+{
+ public class FieldXY
+ {
+ public const int DefaultSeparation = 4;
+
+ public const string ShortenedFormatString = "0.###";
+
+ public FieldXY(TextBox xBox, TextBox yBox, CheckBox lockCheckBox, Form containingForm, double defaultData, AccelCharts accelCharts)
+ {
+ XField = new Field(xBox, containingForm, defaultData);
+ YField = new Field(yBox, containingForm, defaultData);
+ YField.FormatString = ShortenedFormatString;
+ LockCheckBox = lockCheckBox;
+ LockCheckBox.CheckedChanged += new System.EventHandler(CheckChanged);
+ AccelCharts = accelCharts;
+
+ XField.Box.Width = (YField.Box.Left + YField.Box.Width - XField.Box.Left - DefaultSeparation) / 2;
+ YField.Box.Width = XField.Box.Width;
+
+ DefaultWidthX = XField.Box.Width;
+ DefaultWidthY = YField.Box.Width;
+
+ YField.Box.Left = XField.Box.Left + XField.Box.Width + DefaultSeparation;
+
+ CombinedWidth = DefaultWidthX + DefaultWidthY + YField.Box.Left - (XField.Box.Left + DefaultWidthX);
+ SetCombined();
+ }
+ public double X
+ {
+ get => XField.Data;
+ }
+
+ public double Y
+ {
+ get
+ {
+ if (Combined)
+ {
+ return X;
+ }
+ else
+ {
+ return YField.Data;
+ }
+ }
+ }
+
+ public CheckBox LockCheckBox { get; }
+
+ public Field XField { get; }
+
+ public Field YField { get; }
+
+ private AccelCharts AccelCharts { get; }
+
+ private bool Combined { get; set; }
+
+ private int DefaultWidthX { get; }
+
+ private int DefaultWidthY { get; }
+
+ private int CombinedWidth { get; }
+
+ private void CheckChanged(object sender, EventArgs e)
+ {
+ if (LockCheckBox.CheckState == CheckState.Checked)
+ {
+ SetCombined();
+ }
+ else
+ {
+ SetSeparate();
+ }
+
+ AccelCharts.RefreshXY();
+ }
+
+ public void SetCombined()
+ {
+ Combined = true;
+ YField.SetToUnavailable();
+ YField.Box.Hide();
+ XField.Box.Width = CombinedWidth;
+ XField.FormatString = Field.DefaultFormatString;
+ }
+
+ public void SetSeparate()
+ {
+ Combined = false;
+
+ XField.Box.Width = DefaultWidthX;
+ YField.Box.Width = DefaultWidthY;
+
+ XField.FormatString = ShortenedFormatString;
+
+ if (XField.State == Field.FieldState.Default)
+ {
+ YField.SetToDefault();
+ }
+ else
+ {
+ YField.SetToEntered(XField.Data);
+ }
+
+ if (XField.Box.Visible)
+ {
+ YField.Box.Show();
+ }
+ }
+
+ public void Show()
+ {
+ XField.Box.Show();
+
+ if (!Combined)
+ {
+ YField.Box.Show();
+ }
+ }
+
+ public void Hide()
+ {
+ XField.Box.Hide();
+ YField.Box.Hide();
+ }
+ }
+}
diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs
new file mode 100644
index 0000000..fea4e2d
--- /dev/null
+++ b/grapher/Models/Mouse/MouseWatcher.cs
@@ -0,0 +1,727 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher.Models.Mouse
+{
+ public class MouseWatcher
+ {
+ /// <summary>
+ /// Enumeration containing HID usage page flags.
+ /// </summary>
+ public enum HIDUsagePage : ushort
+ {
+ /// <summary>Unknown usage page.</summary>
+ Undefined = 0x00,
+ /// <summary>Generic desktop controls.</summary>
+ Generic = 0x01,
+ /// <summary>Simulation controls.</summary>
+ Simulation = 0x02,
+ /// <summary>Virtual reality controls.</summary>
+ VR = 0x03,
+ /// <summary>Sports controls.</summary>
+ Sport = 0x04,
+ /// <summary>Games controls.</summary>
+ Game = 0x05,
+ /// <summary>Keyboard controls.</summary>
+ Keyboard = 0x07,
+ /// <summary>LED controls.</summary>
+ LED = 0x08,
+ /// <summary>Button.</summary>
+ Button = 0x09,
+ /// <summary>Ordinal.</summary>
+ Ordinal = 0x0A,
+ /// <summary>Telephony.</summary>
+ Telephony = 0x0B,
+ /// <summary>Consumer.</summary>
+ Consumer = 0x0C,
+ /// <summary>Digitizer.</summary>
+ Digitizer = 0x0D,
+ /// <summary>Physical interface device.</summary>
+ PID = 0x0F,
+ /// <summary>Unicode.</summary>
+ Unicode = 0x10,
+ /// <summary>Alphanumeric display.</summary>
+ AlphaNumeric = 0x14,
+ /// <summary>Medical instruments.</summary>
+ Medical = 0x40,
+ /// <summary>Monitor page 0.</summary>
+ MonitorPage0 = 0x80,
+ /// <summary>Monitor page 1.</summary>
+ MonitorPage1 = 0x81,
+ /// <summary>Monitor page 2.</summary>
+ MonitorPage2 = 0x82,
+ /// <summary>Monitor page 3.</summary>
+ MonitorPage3 = 0x83,
+ /// <summary>Power page 0.</summary>
+ PowerPage0 = 0x84,
+ /// <summary>Power page 1.</summary>
+ PowerPage1 = 0x85,
+ /// <summary>Power page 2.</summary>
+ PowerPage2 = 0x86,
+ /// <summary>Power page 3.</summary>
+ PowerPage3 = 0x87,
+ /// <summary>Bar code scanner.</summary>
+ BarCode = 0x8C,
+ /// <summary>Scale page.</summary>
+ Scale = 0x8D,
+ /// <summary>Magnetic strip reading devices.</summary>
+ MSR = 0x8E
+ }
+
+ /// <summary>
+ /// Enumeration containing the HID usage values.
+ /// </summary>
+ public enum HIDUsage : ushort
+ {
+ /// <summary></summary>
+ Pointer = 0x01,
+ /// <summary></summary>
+ Mouse = 0x02,
+ /// <summary></summary>
+ Joystick = 0x04,
+ /// <summary></summary>
+ Gamepad = 0x05,
+ /// <summary></summary>
+ Keyboard = 0x06,
+ /// <summary></summary>
+ Keypad = 0x07,
+ /// <summary></summary>
+ SystemControl = 0x80,
+ /// <summary></summary>
+ X = 0x30,
+ /// <summary></summary>
+ Y = 0x31,
+ /// <summary></summary>
+ Z = 0x32,
+ /// <summary></summary>
+ RelativeX = 0x33,
+ /// <summary></summary>
+ RelativeY = 0x34,
+ /// <summary></summary>
+ RelativeZ = 0x35,
+ /// <summary></summary>
+ Slider = 0x36,
+ /// <summary></summary>
+ Dial = 0x37,
+ /// <summary></summary>
+ Wheel = 0x38,
+ /// <summary></summary>
+ HatSwitch = 0x39,
+ /// <summary></summary>
+ CountedBuffer = 0x3A,
+ /// <summary></summary>
+ ByteCount = 0x3B,
+ /// <summary></summary>
+ MotionWakeup = 0x3C,
+ /// <summary></summary>
+ VX = 0x40,
+ /// <summary></summary>
+ VY = 0x41,
+ /// <summary></summary>
+ VZ = 0x42,
+ /// <summary></summary>
+ VBRX = 0x43,
+ /// <summary></summary>
+ VBRY = 0x44,
+ /// <summary></summary>
+ VBRZ = 0x45,
+ /// <summary></summary>
+ VNO = 0x46,
+ /// <summary></summary>
+ SystemControlPower = 0x81,
+ /// <summary></summary>
+ SystemControlSleep = 0x82,
+ /// <summary></summary>
+ SystemControlWake = 0x83,
+ /// <summary></summary>
+ SystemControlContextMenu = 0x84,
+ /// <summary></summary>
+ SystemControlMainMenu = 0x85,
+ /// <summary></summary>
+ SystemControlApplicationMenu = 0x86,
+ /// <summary></summary>
+ SystemControlHelpMenu = 0x87,
+ /// <summary></summary>
+ SystemControlMenuExit = 0x88,
+ /// <summary></summary>
+ SystemControlMenuSelect = 0x89,
+ /// <summary></summary>
+ SystemControlMenuRight = 0x8A,
+ /// <summary></summary>
+ SystemControlMenuLeft = 0x8B,
+ /// <summary></summary>
+ SystemControlMenuUp = 0x8C,
+ /// <summary></summary>
+ SystemControlMenuDown = 0x8D,
+ /// <summary></summary>
+ KeyboardNoEvent = 0x00,
+ /// <summary></summary>
+ KeyboardRollover = 0x01,
+ /// <summary></summary>
+ KeyboardPostFail = 0x02,
+ /// <summary></summary>
+ KeyboardUndefined = 0x03,
+ /// <summary></summary>
+ KeyboardaA = 0x04,
+ /// <summary></summary>
+ KeyboardzZ = 0x1D,
+ /// <summary></summary>
+ Keyboard1 = 0x1E,
+ /// <summary></summary>
+ Keyboard0 = 0x27,
+ /// <summary></summary>
+ KeyboardLeftControl = 0xE0,
+ /// <summary></summary>
+ KeyboardLeftShift = 0xE1,
+ /// <summary></summary>
+ KeyboardLeftALT = 0xE2,
+ /// <summary></summary>
+ KeyboardLeftGUI = 0xE3,
+ /// <summary></summary>
+ KeyboardRightControl = 0xE4,
+ /// <summary></summary>
+ KeyboardRightShift = 0xE5,
+ /// <summary></summary>
+ KeyboardRightALT = 0xE6,
+ /// <summary></summary>
+ KeyboardRightGUI = 0xE7,
+ /// <summary></summary>
+ KeyboardScrollLock = 0x47,
+ /// <summary></summary>
+ KeyboardNumLock = 0x53,
+ /// <summary></summary>
+ KeyboardCapsLock = 0x39,
+ /// <summary></summary>
+ KeyboardF1 = 0x3A,
+ /// <summary></summary>
+ KeyboardF12 = 0x45,
+ /// <summary></summary>
+ KeyboardReturn = 0x28,
+ /// <summary></summary>
+ KeyboardEscape = 0x29,
+ /// <summary></summary>
+ KeyboardDelete = 0x2A,
+ /// <summary></summary>
+ KeyboardPrintScreen = 0x46,
+ /// <summary></summary>
+ LEDNumLock = 0x01,
+ /// <summary></summary>
+ LEDCapsLock = 0x02,
+ /// <summary></summary>
+ LEDScrollLock = 0x03,
+ /// <summary></summary>
+ LEDCompose = 0x04,
+ /// <summary></summary>
+ LEDKana = 0x05,
+ /// <summary></summary>
+ LEDPower = 0x06,
+ /// <summary></summary>
+ LEDShift = 0x07,
+ /// <summary></summary>
+ LEDDoNotDisturb = 0x08,
+ /// <summary></summary>
+ LEDMute = 0x09,
+ /// <summary></summary>
+ LEDToneEnable = 0x0A,
+ /// <summary></summary>
+ LEDHighCutFilter = 0x0B,
+ /// <summary></summary>
+ LEDLowCutFilter = 0x0C,
+ /// <summary></summary>
+ LEDEqualizerEnable = 0x0D,
+ /// <summary></summary>
+ LEDSoundFieldOn = 0x0E,
+ /// <summary></summary>
+ LEDSurroundFieldOn = 0x0F,
+ /// <summary></summary>
+ LEDRepeat = 0x10,
+ /// <summary></summary>
+ LEDStereo = 0x11,
+ /// <summary></summary>
+ LEDSamplingRateDirect = 0x12,
+ /// <summary></summary>
+ LEDSpinning = 0x13,
+ /// <summary></summary>
+ LEDCAV = 0x14,
+ /// <summary></summary>
+ LEDCLV = 0x15,
+ /// <summary></summary>
+ LEDRecordingFormatDet = 0x16,
+ /// <summary></summary>
+ LEDOffHook = 0x17,
+ /// <summary></summary>
+ LEDRing = 0x18,
+ /// <summary></summary>
+ LEDMessageWaiting = 0x19,
+ /// <summary></summary>
+ LEDDataMode = 0x1A,
+ /// <summary></summary>
+ LEDBatteryOperation = 0x1B,
+ /// <summary></summary>
+ LEDBatteryOK = 0x1C,
+ /// <summary></summary>
+ LEDBatteryLow = 0x1D,
+ /// <summary></summary>
+ LEDSpeaker = 0x1E,
+ /// <summary></summary>
+ LEDHeadset = 0x1F,
+ /// <summary></summary>
+ LEDHold = 0x20,
+ /// <summary></summary>
+ LEDMicrophone = 0x21,
+ /// <summary></summary>
+ LEDCoverage = 0x22,
+ /// <summary></summary>
+ LEDNightMode = 0x23,
+ /// <summary></summary>
+ LEDSendCalls = 0x24,
+ /// <summary></summary>
+ LEDCallPickup = 0x25,
+ /// <summary></summary>
+ LEDConference = 0x26,
+ /// <summary></summary>
+ LEDStandBy = 0x27,
+ /// <summary></summary>
+ LEDCameraOn = 0x28,
+ /// <summary></summary>
+ LEDCameraOff = 0x29,
+ /// <summary></summary>
+ LEDOnLine = 0x2A,
+ /// <summary></summary>
+ LEDOffLine = 0x2B,
+ /// <summary></summary>
+ LEDBusy = 0x2C,
+ /// <summary></summary>
+ LEDReady = 0x2D,
+ /// <summary></summary>
+ LEDPaperOut = 0x2E,
+ /// <summary></summary>
+ LEDPaperJam = 0x2F,
+ /// <summary></summary>
+ LEDRemote = 0x30,
+ /// <summary></summary>
+ LEDForward = 0x31,
+ /// <summary></summary>
+ LEDReverse = 0x32,
+ /// <summary></summary>
+ LEDStop = 0x33,
+ /// <summary></summary>
+ LEDRewind = 0x34,
+ /// <summary></summary>
+ LEDFastForward = 0x35,
+ /// <summary></summary>
+ LEDPlay = 0x36,
+ /// <summary></summary>
+ LEDPause = 0x37,
+ /// <summary></summary>
+ LEDRecord = 0x38,
+ /// <summary></summary>
+ LEDError = 0x39,
+ /// <summary></summary>
+ LEDSelectedIndicator = 0x3A,
+ /// <summary></summary>
+ LEDInUseIndicator = 0x3B,
+ /// <summary></summary>
+ LEDMultiModeIndicator = 0x3C,
+ /// <summary></summary>
+ LEDIndicatorOn = 0x3D,
+ /// <summary></summary>
+ LEDIndicatorFlash = 0x3E,
+ /// <summary></summary>
+ LEDIndicatorSlowBlink = 0x3F,
+ /// <summary></summary>
+ LEDIndicatorFastBlink = 0x40,
+ /// <summary></summary>
+ LEDIndicatorOff = 0x41,
+ /// <summary></summary>
+ LEDFlashOnTime = 0x42,
+ /// <summary></summary>
+ LEDSlowBlinkOnTime = 0x43,
+ /// <summary></summary>
+ LEDSlowBlinkOffTime = 0x44,
+ /// <summary></summary>
+ LEDFastBlinkOnTime = 0x45,
+ /// <summary></summary>
+ LEDFastBlinkOffTime = 0x46,
+ /// <summary></summary>
+ LEDIndicatorColor = 0x47,
+ /// <summary></summary>
+ LEDRed = 0x48,
+ /// <summary></summary>
+ LEDGreen = 0x49,
+ /// <summary></summary>
+ LEDAmber = 0x4A,
+ /// <summary></summary>
+ LEDGenericIndicator = 0x3B,
+ /// <summary></summary>
+ TelephonyPhone = 0x01,
+ /// <summary></summary>
+ TelephonyAnsweringMachine = 0x02,
+ /// <summary></summary>
+ TelephonyMessageControls = 0x03,
+ /// <summary></summary>
+ TelephonyHandset = 0x04,
+ /// <summary></summary>
+ TelephonyHeadset = 0x05,
+ /// <summary></summary>
+ TelephonyKeypad = 0x06,
+ /// <summary></summary>
+ TelephonyProgrammableButton = 0x07,
+ /// <summary></summary>
+ SimulationRudder = 0xBA,
+ /// <summary></summary>
+ SimulationThrottle = 0xBB
+ }
+
+ /// <summary>Enumeration containing flags for a raw input device.</summary>
+ [Flags()]
+ public enum RawInputDeviceFlags
+ {
+ /// <summary>No flags.</summary>
+ None = 0,
+ /// <summary>If set, this removes the top level collection from the inclusion list. This tells the operating system to stop reading from a device which matches the top level collection.</summary>
+ Remove = 0x00000001,
+ /// <summary>If set, this specifies the top level collections to exclude when reading a complete usage page. This flag only affects a TLC whose usage page is already specified with PageOnly.</summary>
+ Exclude = 0x00000010,
+ /// <summary>If set, this specifies all devices whose top level collection is from the specified usUsagePage. Note that Usage must be zero. To exclude a particular top level collection, use Exclude.</summary>
+ PageOnly = 0x00000020,
+ /// <summary>If set, this prevents any devices specified by UsagePage or Usage from generating legacy messages. This is only for the mouse and keyboard.</summary>
+ NoLegacy = 0x00000030,
+ /// <summary>If set, this enables the caller to receive the input even when the caller is not in the foreground. Note that WindowHandle must be specified.</summary>
+ InputSink = 0x00000100,
+ /// <summary>If set, the mouse button click does not activate the other window.</summary>
+ CaptureMouse = 0x00000200,
+ /// <summary>If set, the application-defined keyboard device hotkeys are not handled. However, the system hotkeys; for example, ALT+TAB and CTRL+ALT+DEL, are still handled. By default, all keyboard hotkeys are handled. NoHotKeys can be specified even if NoLegacy is not specified and WindowHandle is NULL.</summary>
+ NoHotKeys = 0x00000200,
+ /// <summary>If set, application keys are handled. NoLegacy must be specified. Keyboard only.</summary>
+ AppKeys = 0x00000400
+ }
+
+ /// <summary>Value type for raw input devices.</summary>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RAWINPUTDEVICE
+ {
+ /// <summary>Top level collection Usage page for the raw input device.</summary>
+ public HIDUsagePage UsagePage;
+ /// <summary>Top level collection Usage for the raw input device. </summary>
+ public HIDUsage Usage;
+ /// <summary>Mode flag that specifies how to interpret the information provided by UsagePage and Usage.</summary>
+ public RawInputDeviceFlags Flags;
+ /// <summary>Handle to the target device. If NULL, it follows the keyboard focus.</summary>
+ public IntPtr WindowHandle;
+ }
+
+ /// <summary>
+ /// Contains the raw input from a device.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RawInput
+ {
+ /// <summary>
+ /// Header for the data.
+ /// </summary>
+ public RAWINPUTHEADER Header;
+ public Union Data;
+ [StructLayout(LayoutKind.Explicit)]
+ public struct Union
+ {
+ /// <summary>
+ /// Mouse raw input data.
+ /// </summary>
+ [FieldOffset(0)]
+ public RawMouse Mouse;
+ /// <summary>
+ /// Keyboard raw input data.
+ /// </summary>
+ [FieldOffset(0)]
+ public RawKeyboard Keyboard;
+ /// <summary>
+ /// HID raw input data.
+ /// </summary>
+ [FieldOffset(0)]
+ public RawHID HID;
+ }
+ }
+
+ /// <summary>
+ /// Value type for raw input from a HID.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RawHID
+ {
+ /// <summary>Size of the HID data in bytes.</summary>
+ public int Size;
+ /// <summary>Number of HID in Data.</summary>
+ public int Count;
+ /// <summary>Data for the HID.</summary>
+ public IntPtr Data;
+ }
+
+ /// <summary>
+ /// Value type for a raw input header.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RAWINPUTHEADER
+ {
+ /// <summary>Type of device the input is coming from.</summary>
+ public RawInputType Type;
+ /// <summary>Size of the packet of data.</summary>
+ public int Size;
+ /// <summary>Handle to the device sending the data.</summary>
+ public IntPtr Device;
+ /// <summary>wParam from the window message.</summary>
+ public IntPtr wParam;
+ }
+
+ /// <summary>
+ /// Enumeration containing the type device the raw input is coming from.
+ /// </summary>
+ public enum RawInputType
+ {
+ /// <summary>
+ /// Mouse input.
+ /// </summary>
+ Mouse = 0,
+
+ /// <summary>
+ /// Keyboard input.
+ /// </summary>
+ Keyboard = 1,
+
+ /// <summary>
+ /// Human interface device input.
+ /// </summary>
+ HID = 2,
+
+ /// <summary>
+ /// Another device that is not the keyboard or the mouse.
+ /// </summary>
+ Other = 3
+ }
+
+ /// <summary>
+ /// Contains information about the state of the mouse.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RawMouse
+ {
+ /// <summary>
+ /// The mouse state.
+ /// </summary>
+ public RawMouseFlags Flags;
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct Data
+ {
+ [FieldOffset(0)]
+ public uint Buttons;
+ /// <summary>
+ /// If the mouse wheel is moved, this will contain the delta amount.
+ /// </summary>
+ [FieldOffset(2)]
+ public ushort ButtonData;
+ /// <summary>
+ /// Flags for the event.
+ /// </summary>
+ [FieldOffset(0)]
+ public RawMouseButtons ButtonFlags;
+ }
+
+ public Data _Data;
+
+ /// <summary>
+ /// Raw button data.
+ /// </summary>
+ public uint RawButtons;
+ /// <summary>
+ /// The motion in the X direction. This is signed relative motion or
+ /// absolute motion, depending on the value of usFlags.
+ /// </summary>
+ public int LastX;
+ /// <summary>
+ /// The motion in the Y direction. This is signed relative motion or absolute motion,
+ /// depending on the value of usFlags.
+ /// </summary>
+ public int LastY;
+ /// <summary>
+ /// The device-specific additional information for the event.
+ /// </summary>
+ public uint ExtraInformation;
+ }
+
+ /// <summary>
+ /// Enumeration containing the flags for raw mouse data.
+ /// </summary>
+ [Flags()]
+ public enum RawMouseFlags
+ : ushort
+ {
+ /// <summary>Relative to the last position.</summary>
+ MoveRelative = 0,
+ /// <summary>Absolute positioning.</summary>
+ MoveAbsolute = 1,
+ /// <summary>Coordinate data is mapped to a virtual desktop.</summary>
+ VirtualDesktop = 2,
+ /// <summary>Attributes for the mouse have changed.</summary>
+ AttributesChanged = 4
+ }
+
+ /// <summary>
+ /// Enumeration containing the button data for raw mouse input.
+ /// </summary>
+ [Flags()]
+ public enum RawMouseButtons
+ : ushort
+ {
+ /// <summary>No button.</summary>
+ None = 0,
+ /// <summary>Left (button 1) down.</summary>
+ LeftDown = 0x0001,
+ /// <summary>Left (button 1) up.</summary>
+ LeftUp = 0x0002,
+ /// <summary>Right (button 2) down.</summary>
+ RightDown = 0x0004,
+ /// <summary>Right (button 2) up.</summary>
+ RightUp = 0x0008,
+ /// <summary>Middle (button 3) down.</summary>
+ MiddleDown = 0x0010,
+ /// <summary>Middle (button 3) up.</summary>
+ MiddleUp = 0x0020,
+ /// <summary>Button 4 down.</summary>
+ Button4Down = 0x0040,
+ /// <summary>Button 4 up.</summary>
+ Button4Up = 0x0080,
+ /// <summary>Button 5 down.</summary>
+ Button5Down = 0x0100,
+ /// <summary>Button 5 up.</summary>
+ Button5Up = 0x0200,
+ /// <summary>Mouse wheel moved.</summary>
+ MouseWheel = 0x0400
+ }
+
+ /// <summary>
+ /// Value type for raw input from a keyboard.
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RawKeyboard
+ {
+ /// <summary>Scan code for key depression.</summary>
+ public short MakeCode;
+ /// <summary>Scan code information.</summary>
+ public RawKeyboardFlags Flags;
+ /// <summary>Reserved.</summary>
+ public short Reserved;
+ /// <summary>Virtual key code.</summary>
+ public ushort VirtualKey;
+ /// <summary>Corresponding window message.</summary>
+ public uint Message;
+ /// <summary>Extra information.</summary>
+ public int ExtraInformation;
+ }
+
+ /// <summary>
+ /// Enumeration containing flags for raw keyboard input.
+ /// </summary>
+ [Flags]
+ public enum RawKeyboardFlags : ushort
+ {
+ /// <summary></summary>
+ KeyMake = 0,
+ /// <summary></summary>
+ KeyBreak = 1,
+ /// <summary></summary>
+ KeyE0 = 2,
+ /// <summary></summary>
+ KeyE1 = 4,
+ /// <summary></summary>
+ TerminalServerSetLED = 8,
+ /// <summary></summary>
+ TerminalServerShadow = 0x10,
+ /// <summary></summary>
+ TerminalServerVKPACKET = 0x20
+ }
+
+ /// <summary>
+ /// Enumeration contanining the command types to issue.
+ /// </summary>
+ public enum RawInputCommand
+ {
+ /// <summary>
+ /// Get input data.
+ /// </summary>
+ Input = 0x10000003,
+ /// <summary>
+ /// Get header data.
+ /// </summary>
+ Header = 0x10000005
+ }
+
+ [DllImport("user32.dll")]
+ public static extern bool RegisterRawInputDevices([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] RAWINPUTDEVICE[] pRawInputDevices, int uiNumDevices, int cbSize);
+
+ /// <summary>
+ /// Function to retrieve raw input data.
+ /// </summary>
+ /// <param name="hRawInput">Handle to the raw input.</param>
+ /// <param name="uiCommand">Command to issue when retrieving data.</param>
+ /// <param name="pData">Raw input data.</param>
+ /// <param name="pcbSize">Number of bytes in the array.</param>
+ /// <param name="cbSizeHeader">Size of the header.</param>
+ /// <returns>0 if successful if pData is null, otherwise number of bytes if pData is not null.</returns>
+ [DllImport("user32.dll")]
+ public static extern int GetRawInputData(IntPtr hRawInput, RawInputCommand uiCommand, out RawInput pData, ref int pcbSize, int cbSizeHeader);
+
+ public MouseWatcher(Form containingForm, Label display, AccelCharts accelCharts)
+ {
+ ContainingForm = containingForm;
+ Display = display;
+ AccelCharts = accelCharts;
+
+ RAWINPUTDEVICE device = new RAWINPUTDEVICE();
+ device.WindowHandle = ContainingForm.Handle;
+ device.UsagePage = HIDUsagePage.Generic;
+ device.Usage = HIDUsage.Mouse;
+ device.Flags = RawInputDeviceFlags.InputSink;
+
+ RAWINPUTDEVICE[] devices = new RAWINPUTDEVICE[1];
+ devices[0] = device;
+ RegisterRawInputDevices(devices, 1, Marshal.SizeOf(typeof(RAWINPUTDEVICE)));
+ PollTime = 1;
+ }
+
+ private Form ContainingForm { get; }
+
+ private Label Display { get; }
+
+ private AccelCharts AccelCharts { get; }
+
+ private double PollTime { get; }
+
+ public void OnMouseMove(int x, int y, double timeInMs)
+ {
+ Display.Text = $"Last (x, y): ({x}, {y})";
+ AccelCharts.MakeDots(x, y, timeInMs);
+ }
+
+ public void ReadMouseMove(Message message)
+ {
+ RawInput rawInput = new RawInput();
+ int outSize = 0;
+ int size = Marshal.SizeOf(typeof(RawInput));
+
+ outSize = GetRawInputData((IntPtr)message.LParam, RawInputCommand.Input, out rawInput, ref size, Marshal.SizeOf(typeof(RAWINPUTHEADER)));
+
+ if (rawInput.Data.Mouse.LastX != 0 || rawInput.Data.Mouse.LastY != 0)
+ {
+ OnMouseMove(rawInput.Data.Mouse.LastX, rawInput.Data.Mouse.LastY, PollTime);
+ }
+
+ }
+ }
+}
diff --git a/grapher/Models/Mouse/PointData.cs b/grapher/Models/Mouse/PointData.cs
new file mode 100644
index 0000000..12a6e73
--- /dev/null
+++ b/grapher/Models/Mouse/PointData.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace grapher.Models.Mouse
+{
+ public class PointData
+ {
+ public PointData()
+ {
+ Lock = new Object();
+ X = new double[] { 0 };
+ Y = new double[] { 0 };
+ }
+
+ public Object Lock { get; }
+
+ private double[] X { get; set; }
+ private double[] Y { get; set; }
+
+ public void Set(double x, double y)
+ {
+ lock(Lock)
+ {
+ X[0] = x;
+ Y[0] = y;
+ }
+ }
+
+ public void Get(out double[] x, out double[] y)
+ {
+ lock(Lock)
+ {
+ x = X;
+ y = Y;
+ }
+ }
+ }
+}
diff --git a/grapher/Models/Options/AccelOptions.cs b/grapher/Models/Options/AccelOptions.cs
new file mode 100644
index 0000000..b233552
--- /dev/null
+++ b/grapher/Models/Options/AccelOptions.cs
@@ -0,0 +1,79 @@
+using grapher.Layouts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher
+{
+ public class AccelOptions
+ {
+ public const int PossibleOptionsCount = 4;
+ public const int PossibleOptionsXYCount = 2;
+
+ public static readonly Dictionary<string, LayoutBase> AccelerationTypes = new List<LayoutBase>
+ {
+ new DefaultLayout(),
+ new LinearLayout(),
+ new ClassicLayout(),
+ new NaturalLayout(),
+ new LogLayout(),
+ new SigmoidLayout(),
+ new PowerLayout(),
+ new OffLayout()
+ }.ToDictionary(k => k.Name);
+
+ public AccelOptions(
+ ComboBox accelDropdown,
+ Option[] options,
+ OptionXY[] optionsXY,
+ Button writeButton)
+ {
+ AccelDropdown = accelDropdown;
+ AccelDropdown.Items.Clear();
+ AccelDropdown.Items.AddRange(AccelerationTypes.Keys.Skip(1).ToArray());
+ AccelDropdown.SelectedIndexChanged += new System.EventHandler(OnIndexChanged);
+
+ if (options.Length > PossibleOptionsCount)
+ {
+ throw new Exception("Layout given too many options.");
+ }
+
+ if (optionsXY.Length > PossibleOptionsXYCount)
+ {
+ throw new Exception("Layout given too many options.");
+ }
+
+ Options = options;
+ OptionsXY = optionsXY;
+ WriteButton = writeButton;
+
+ Layout("Default");
+ }
+
+ public Button WriteButton { get; }
+
+ public ComboBox AccelDropdown { get; }
+
+ public int AccelerationIndex { get; private set; }
+
+ public Option[] Options { get; }
+
+ public OptionXY[] OptionsXY { get; }
+
+ private void OnIndexChanged(object sender, EventArgs e)
+ {
+ var accelerationTypeString = AccelDropdown.SelectedItem.ToString();
+ Layout(accelerationTypeString);
+ }
+
+ private void Layout(string type)
+ {
+ var accelerationType = AccelerationTypes[type];
+ AccelerationIndex = accelerationType.Index;
+ accelerationType.Layout(Options, OptionsXY, WriteButton);
+ }
+ }
+}
diff --git a/grapher/Models/Options/CapOptions.cs b/grapher/Models/Options/CapOptions.cs
new file mode 100644
index 0000000..2ee7f6b
--- /dev/null
+++ b/grapher/Models/Options/CapOptions.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher
+{
+ public class CapOptions
+ {
+ public CapOptions(
+ ToolStripMenuItem sensitivityCapCheck,
+ ToolStripMenuItem velocityGainCapCheck,
+ OptionXY capOption,
+ OptionXY weightOption)
+ {
+
+ SensitivityCapCheck = sensitivityCapCheck;
+ VelocityGainCapCheck = velocityGainCapCheck;
+ CapOption = capOption;
+ WeightOption = weightOption;
+
+ SensitivityCapCheck.Click += new System.EventHandler(OnSensitivityCapCheckClick);
+ VelocityGainCapCheck.Click += new System.EventHandler(OnVelocityGainCapCheckClick);
+
+ SensitivityCapCheck.CheckedChanged += new System.EventHandler(OnSensitivityCapCheckedChange);
+ VelocityGainCapCheck.CheckedChanged += new System.EventHandler(OnVelocityGainCapCheckedChange);
+
+ EnableSensitivityCap();
+ }
+
+ ToolStripMenuItem SensitivityCapCheck { get; }
+
+ ToolStripMenuItem VelocityGainCapCheck { get; }
+
+ OptionXY CapOption { get; }
+
+ OptionXY WeightOption { get; }
+
+ public double SensitivityCapX {
+ get
+ {
+ if (IsSensitivityGain)
+ {
+ return CapOption.Fields.X;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ public double SensitivityCapY {
+ get
+ {
+ if (IsSensitivityGain)
+ {
+ return CapOption.Fields.Y;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ public double VelocityGainCap {
+ get
+ {
+ if (IsSensitivityGain)
+ {
+ return 0;
+ }
+ else
+ {
+ return CapOption.Fields.X;
+ }
+ }
+ }
+
+ public bool IsSensitivityGain { get; private set; }
+
+ void OnSensitivityCapCheckClick(object sender, EventArgs e)
+ {
+ if (!SensitivityCapCheck.Checked)
+ {
+ VelocityGainCapCheck.Checked = false;
+ SensitivityCapCheck.Checked = true;
+ }
+ }
+
+ void OnVelocityGainCapCheckClick(object sender, EventArgs e)
+ {
+ if (!VelocityGainCapCheck.Checked)
+ {
+ VelocityGainCapCheck.Checked = true;
+ SensitivityCapCheck.Checked = false;
+ }
+ }
+
+ void OnSensitivityCapCheckedChange(object sender, EventArgs e)
+ {
+ if (SensitivityCapCheck.Checked == true)
+ {
+ EnableSensitivityCap();
+ }
+ }
+
+ void OnVelocityGainCapCheckedChange(object sender, EventArgs e)
+ {
+ if (SensitivityCapCheck.Checked == true)
+ {
+ EnableVelocityGainCap();
+ }
+ }
+
+ void EnableSensitivityCap()
+ {
+ IsSensitivityGain = true;
+ CapOption.Fields.LockCheckBox.Enabled = true;
+ WeightOption.Fields.LockCheckBox.Enabled = true;
+ CapOption.SetName("Sensitivity Cap");
+ }
+
+ void EnableVelocityGainCap()
+ {
+ IsSensitivityGain = false;
+ CapOption.Fields.LockCheckBox.Checked = true;
+ CapOption.Fields.LockCheckBox.Enabled = false;
+ WeightOption.Fields.LockCheckBox.Checked = true;
+ WeightOption.Fields.LockCheckBox.Enabled = false;
+ CapOption.SetName("Velocity Gain Cap");
+ }
+ }
+}
diff --git a/grapher/Models/Options/Option.cs b/grapher/Models/Options/Option.cs
new file mode 100644
index 0000000..eb5105e
--- /dev/null
+++ b/grapher/Models/Options/Option.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher
+{
+ public class Option
+ {
+ public Option(Field field, Label label)
+ {
+ Field = field;
+ Label = label;
+ }
+
+ public Option(TextBox box, Form containingForm, double defaultData, Label label)
+ : this(new Field(box, containingForm, defaultData), label)
+ {
+ }
+
+ public Option(TextBox box, Form containingForm, double defaultData, Label label, string startingName)
+ : this(box, containingForm, defaultData, label)
+ {
+ SetName(startingName);
+ }
+
+ public Field Field { get; }
+
+ public Label Label { get; }
+
+ public void SetName(string name)
+ {
+ Label.Text = name;
+ Label.Left = Convert.ToInt32((Field.Box.Left / 2.0) - (Label.Width / 2.0));
+ }
+
+ public void Hide()
+ {
+ Field.Box.Hide();
+ Label.Hide();
+ }
+
+ public void Show()
+ {
+ Field.Box.Show();
+ Label.Show();
+ }
+
+ public void Show(string name)
+ {
+ SetName(name);
+
+ Show();
+ }
+ }
+}
diff --git a/grapher/Models/Options/OptionXY.cs b/grapher/Models/Options/OptionXY.cs
new file mode 100644
index 0000000..90a46d7
--- /dev/null
+++ b/grapher/Models/Options/OptionXY.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher
+{
+ public class OptionXY
+ {
+ public OptionXY(FieldXY fields, Label label)
+ {
+ Fields = fields;
+ Label = label;
+ }
+
+ public OptionXY(
+ TextBox xBox,
+ TextBox yBox,
+ CheckBox lockCheckBox,
+ Form containingForm,
+ double defaultData,
+ Label label,
+ AccelCharts accelCharts)
+ : this(new FieldXY(xBox, yBox, lockCheckBox, containingForm, defaultData, accelCharts), label)
+ {
+ }
+
+ public OptionXY(
+ TextBox xBox,
+ TextBox yBox,
+ CheckBox lockCheckBox,
+ Form containingForm,
+ double defaultData,
+ Label label,
+ string startingName,
+ AccelCharts accelCharts):
+ this(
+ xBox,
+ yBox,
+ lockCheckBox,
+ containingForm,
+ defaultData,
+ label,
+ accelCharts)
+ {
+ SetName(startingName);
+ }
+
+ public FieldXY Fields { get; }
+
+ public Label Label { get; }
+
+ public void SetName(string name)
+ {
+ Label.Text = name;
+ Label.Left = Convert.ToInt32((Fields.XField.Box.Left / 2.0) - (Label.Width / 2.0));
+ }
+
+ public void Hide()
+ {
+ Fields.Hide();
+ Fields.LockCheckBox.Hide();
+ Label.Hide();
+ }
+
+ public void Show()
+ {
+ Fields.Show();
+ Fields.LockCheckBox.Show();
+ Label.Show();
+ }
+
+ public void Show(string name)
+ {
+ SetName(name);
+
+ Show();
+ }
+
+ }
+}