diff options
| author | JacobPalecki <[email protected]> | 2021-01-20 20:13:33 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-01-20 20:13:33 -0800 |
| commit | 5b6479013c8f35df933dd57c680063f4db1e4028 (patch) | |
| tree | 60dd7c67a0f163457da2519b42553382a39a591b | |
| parent | show custom dialog on bad input (#63) (diff) | |
| parent | Guard against bad anisotropy args (diff) | |
| download | rawaccel-5b6479013c8f35df933dd57c680063f4db1e4028.tar.xz rawaccel-5b6479013c8f35df933dd57c680063f4db1e4028.zip | |
Merge pull request #65 from JacobPalecki/Directional
Directionality Features + Graph Fidelity
37 files changed, 1439 insertions, 456 deletions
diff --git a/common/accel-base.hpp b/common/accel-base.hpp index b15d695..42b3bb1 100644 --- a/common/accel-base.hpp +++ b/common/accel-base.hpp @@ -17,6 +17,11 @@ namespace rawaccel { double speed_cap = 0; }; + struct domain_args { + vec2d domain_weights = { 1, 1 }; + double lp_norm = 2; + }; + template <typename Func> struct accel_val_base { bool legacy_offset = false; diff --git a/common/accel-motivity.hpp b/common/accel-motivity.hpp index f88320b..246cf37 100644 --- a/common/accel-motivity.hpp +++ b/common/accel-motivity.hpp @@ -55,28 +55,40 @@ namespace rawaccel { inline void fill(si_pair* lookup) const { double lookup_speed = 0; + double integral_interval = 0; double gain_integral_speed = 0; + double gain_integral_speed_prev = 0; double gain = 0; double intercept = 0; double output = 0; + double output_prev = 0; double x = -2; + double logarithm_interval = 0.01; + double integral_intervals_per_speed = 10; + double integral_interval_factor = pow(10, logarithm_interval) / integral_intervals_per_speed; + lookup[0] = {}; for (size_t i = 1; i < LUT_SIZE; i++) { - x += 0.01; + x += logarithm_interval; + // Each lookup speed will be 10^0.01 = 2.33% higher than the previous + // To get 10 integral intervals per speed, set interval to 0.233% lookup_speed = pow(10, x); + integral_interval = lookup_speed * integral_interval_factor; while (gain_integral_speed < lookup_speed) { - gain_integral_speed += 0.001; + output_prev = output; + gain_integral_speed_prev = gain_integral_speed; + gain_integral_speed += integral_interval; gain = operator()(gain_integral_speed); - output += gain * 0.001; + output += gain * integral_interval; } - intercept = output - gain * lookup_speed; + intercept = output_prev - gain_integral_speed_prev * gain; lookup[i] = { gain, intercept }; } diff --git a/common/rawaccel-settings.h b/common/rawaccel-settings.h index eb7c49a..6fa2aa9 100644 --- a/common/rawaccel-settings.h +++ b/common/rawaccel-settings.h @@ -24,6 +24,8 @@ namespace rawaccel { vec2<accel_args> argsv; vec2d sens = { 1, 1 }; vec2d dir_multipliers = {}; + domain_args domain_args = {}; + vec2d range_weights = { 1, 1 }; milliseconds time_min = DEFAULT_TIME_MIN; wchar_t device_id[MAX_DEV_ID_LEN] = {0}; }; diff --git a/common/rawaccel-version.h b/common/rawaccel-version.h index 9470ca0..0e02036 100644 --- a/common/rawaccel-version.h +++ b/common/rawaccel-version.h @@ -1,7 +1,7 @@ #pragma once #define RA_VER_MAJOR 1 -#define RA_VER_MINOR 3 +#define RA_VER_MINOR 4 #define RA_VER_PATCH 0 #define RA_MIN_OS "Win7" @@ -20,7 +20,7 @@ namespace rawaccel { }; #ifndef _KERNEL_MODE - inline constexpr version_t min_driver_version = { 1, 3, 0 }; + inline constexpr version_t min_driver_version = { 1, 4, 0 }; #endif } diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index 3e049cb..c617bed 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -225,12 +225,90 @@ namespace rawaccel { accelerator() = default; }; + struct weighted_distance { + double p = 2.0; + double p_inverse = 0.5; + bool lp_norm_infinity = false; + double sigma_x = 1.0; + double sigma_y = 1.0; + + weighted_distance(domain_args args) + { + sigma_x = args.domain_weights.x; + sigma_y = args.domain_weights.y; + if (args.lp_norm <= 0) + { + lp_norm_infinity = true; + p = 0.0; + p_inverse = 0.0; + } + else + { + lp_norm_infinity = false; + p = args.lp_norm; + p_inverse = 1 / args.lp_norm; + } + } + + double calculate(double x, double y) + { + double abs_x = fabs(x); + double abs_y = fabs(y); + + if (lp_norm_infinity) + { + return abs_x > abs_y ? abs_x : abs_y; + } + + double x_scaled = abs_x * sigma_x; + double y_scaled = abs_y * sigma_y; + return pow(pow(x_scaled, p) + pow(y_scaled, p), p_inverse); + } + + weighted_distance() = default; + }; + + struct direction_weight { + double diff = 0.0; + double start = 1.0; + bool should_apply = false; + + direction_weight(vec2d thetas) + { + diff = thetas.y - thetas.x; + start = thetas.x; + + if (diff != 0) + { + should_apply = true; + } + else + { + should_apply = false; + } + } + + inline double atan_scale(double x, double y) + { + return M_2_PI * atan2(fabs(y), fabs(x)); + } + + double apply(double x, double y) + { + return atan_scale(x, y) * diff + start; + } + + direction_weight() = default; + }; + /// <summary> Struct to hold variables and methods for modifying mouse input </summary> struct mouse_modifier { bool apply_rotate = false; bool apply_accel = false; bool combine_magnitudes = true; rotator rotate; + weighted_distance distance; + direction_weight directional; vec2<accelerator> accels; vec2d sensitivity = { 1, 1 }; vec2d directional_multipliers = {}; @@ -257,6 +335,10 @@ namespace rawaccel { accels.x = accelerator(args.argsv.x, args.modes.x, luts.x); accels.y = accelerator(args.argsv.y, args.modes.y, luts.y); + + distance = weighted_distance(args.domain_args); + directional = direction_weight(args.range_weights); + apply_accel = true; } @@ -276,9 +358,15 @@ namespace rawaccel { milliseconds time = time_supp(); if (combine_magnitudes) { - double mag = sqrtsd(movement.x * movement.x + movement.y * movement.y); + double mag = distance.calculate(movement.x, movement.y); double speed = mag / time; double scale = accels.x.apply(speed); + + if (directional.should_apply) + { + scale = (scale - 1)*directional.apply(movement.x, movement.y) + 1; + } + movement.x *= scale; movement.y *= scale; } diff --git a/doc/Guide.md b/doc/Guide.md index 54fbbc8..df43e94 100644 --- a/doc/Guide.md +++ b/doc/Guide.md @@ -19,9 +19,9 @@ Visit the [Releases page](https://github.com/a1xd/rawaccel/releases) and navigat The Raw Accel driver and GUI's workings and exposed parameters are based on our understanding of mouse acceleration. Our understanding includes the concepts of "gain", "whole vs by component", and "anisotropy." For clarity, we will outline this understanding here. Those uninterested can skip to Features below. ### Measurements from Input Speed -Raw Accel, like any mouse modification program, works by acting on a passed-in (x,y) input in order to pass back out an (x,y) output. The GUI program creates charts by feeding a set of (x,y) inputs to the driver code to receive a set of (x,y) outputs. The following measurements, available as charts in Raw Accel, are then found from the outputs: +Raw Accel, like any mouse modification program, works by acting on a passed-in (x,y) input in order to pass back out an (x,y) output. The GUI program creates charts by feeding a set of (x,y) inputs and times to the driver code to receive a set of (x,y) outputs. The following measurements, available as charts in Raw Accel, are then found from the outputs: -- **Sensitivity**: The ratio of the output speed to the input speed. The "sensitivity" parameter in the program is a multiplier used on the post-calculation output vector. +- **Sensitivity**: The ratio of the output speed to the input speed. The "sensitivity multiplier" parameter in the program is a multiplier used on the post-calculation output vector. - **(Output) Velocity**: The speed of the final output vector. The output vs input velocity curve is perhaps the most important relationship in a particular setup because it directly describes the output for any given input. (We use "speed" and "velocity" interchangeably, and are aware of the difference elsewhere.) - **Gain**: The slope of the output vs input velocity curve. It answers the question: "if I move my hand a little faster, how much faster will my cursor move?" The relationship between gain and sensitivity is that if gain is continuous, so is sensitivity. The reverse is not necessarily true, so keeping the gain "nice" ensures "nice" sensitivity but not vice versa. - For the mathematically inclined: for input speed "v" and Output Velocity f(v), Sensitivity is f(v)/v and Gain is f'(v) = d/dv(f(v)). @@ -40,17 +40,44 @@ Then our input speed is sqrt(30^2 + 40^2) = 50 counts/ms. Our accelerated sensit  ### Horizontal and Vertical -If you wish, horizontal and vertical components of a mouse movement can be treated completely separately in Raw Accel. In the above example, they are not treated separately; rather they are "combined" by using the magnitude if the input vector: *sqrt(30^2 + 40^2) = 50 counts/ms*. This is called "Whole" application because the whole speed of the input is used. Application styles include: +Due to the mechanics of holding a mouse on a desk, users generally move their mouses horizontally (left and right) differently than they move them vertically (forward and back), with more freedom for the wrist and\or arm to move the mouse horizontally than vertically. A user may then desire for various aspects of their output to change based on the direction of their input. For settings which allow this we have co-opted the term "anisotropy", which is "the property of a material which allows it to change or assume different properties in different directions." -- Whole: magnitude of vector is input to sensitivity calculation, and applied to whole vector, as in example above. - - (out_x, out_y) = (in_x\*sens_x, in_y\*sens_y) \* f(sqrt(in_x^2 + in_y^2)), where f(v) is our sensitvity function - - Separate horizontal and vertical sensitivites still feel great in this mode. -- By Component: The horizontal components are separated and each is given as input to the sensitivity calculation to multiplied by itself before being recombined at output. +In the above "example" section, the x and y inputs are not treated separately; rather they are "combined" by using the magnitude if the input vector: *sqrt(30^2 + 40^2) = 50 counts/ms*. This is called "Whole" application because the whole speed of the input is used and the result is applied to the whole vector. Application styles include: + +#### ***Whole*** +In this case, the magnitude of the input vector is input to sensitivity calculation, and applied to whole vector, as in example above. + - (out_x, out_y) = (in_x\*sens_x, in_y\*sens_y) \* f(sqrt(in_x^2 + in_y^2)), where f(v) is our sensitivity function + +Separate horizontal and vertical sensitivites still feel great in this mode. (For the mathematically inclined, that's because differing horizontal and vertical sensitivities create a vector field without curl or other oddities.) + +There are anisotropic settings for whole mode. +- **Range**. This scales the range of the sensitivity curve around 1 for the horizontal or vertical direction. + - If a given curve varies from 1 to 2 sensitivity, then a range_y of 0.5 would mean that vertical movements vary from 1 to 1.5 sensitivity instead. +- **Domain**. This scales the domain of curve around 0 for the horizontal or vertical direction. + - If a given curve has an offset at 5 count/ms and a cap that is hit at 15 counts/ms, then a domain_y of 2 would mean that vertical movements hit the offset at 2.5 counts/ms and the cap at 7.5 counts/ms instead. +- **Lp Norm**. The distance calculation can be generalized to ((in_x)^p + (in_y)^p)^(1/p)), bringing the calculation into [Lp space](https://en.wikipedia.org/wiki/Lp_space). + - p = 2 is then the "real world" value, yielding the pythagorean theorem as the distance calculation. + - Increasing p makes distances for diagonal movements (where in_x and in_y are close) smaller, and increases the dominance of the larger of the two in determining the distance. + - We recommend almost everyone leave this at 2. + + + +With all anisotropic settings considered, the full formula looks like: +- (out_x, out_y) = (in_x\*sens_x, in_y\*sens_y) \* ((f(((domain_x\*in_x)^p + (domain_y\*in_y)^p)^(1/p)) - 1) \* ((2 / Pi) \* arctan(abs(in_y/in_x)) \* (range_y - range_x) + range_x) + 1, where f(v) is our sensitivity function + +This can be more easily understood as +- (out_x, out_y) = (in_x\*sens_x, in_y\*sens_y) \* ((f( domain-weighted lp-space speed) - 1) \* (directional weight) + 1), where f(v) is our sensitivity function + +This formula gaurantees the smooth transition from the horizontal to vertical curve and vice versa as the user moves their hand diagonally. + +#### ***By Component*** +In this case, the horizontal components are separated and each is given as input to the sensitivity calculation to multiplied by itself before being recombined at output. - (out_x, out_y) = (in_x \* f(in_x) \* sens_x, in_y \* f(in_y) \* sens_y)) -- Anisotropy: This is a sub-mode of By Component in which the x and y can have separate sensitivity curves. - - (out_x, out_y) = (in_x \* f(in_x) \* sens_x, in_y \* g(in_y) \* sens_y)) where g(v) is some other sensitivity function. + - You can also do: (out_x, out_y) = (in_x \* f(in_x) \* sens_x, in_y \* g(in_y) \* sens_y)) where g(v) is some other sensitivity function. + +All anisotropic effects for By Component mode can be achieved by setting different x and y curves. -The authors of this program feel that Whole is the best style for most users, but that users who play games which have very separate horizontal and vertical actions to manage (such as tracking an enemy and controlling recoil) might benefit from trying By Component or Anisotropy. +The authors of this program feel that Whole is the best style for most users, but that users who play games which have very separate horizontal and vertical actions to manage (such as tracking an enemy and controlling recoil) might benefit from trying By Component. By Component may seem more flexible, but it is precisely the restrictions imposed by Whole (no curl) that make it smoother. ## Features @@ -66,14 +93,14 @@ Our acceleration functions generally have sensitivity functions that start at 1 Weight is primarily a quick and dirty way to test a new curve. It also can be given a negative value to allow negative acceleration. Most acceleration styles could just change the parameters to have the same affect as setting a weight. Some curves, like the logarithm style, can achieve a greater range of shapes by changing weight. -### By Component & Anisotropy -See "Horizontal and Vertical" in the philosophy section to understand what these do. +### Anisotropy +See "Horizontal and Vertical" in the philosophy section to understand what these options do. ### Last Mouse Move The Raw Accel GUI reads the output of the raw input stream, and thus the output of the Raw Accel Driver, and displays on the graphs red points corresponding to the last mouse movements. These calulations should be fast and your graph responsive, but it comes at the cost of higher CPU usage due to needing to refresh the graph often. This feature can be turned off in the "Charts" menu. ### Scale by DPI and Poll Rate -This option does not scale your acceleration curve in any way. Rather, it scales the set of points used to graph your curve, and shows you a window of input speed relevant for your chosen DPI and Poll Rate. The poll rate is also used to determine the Last Mouse Move points and therefore should be set for accuracy in that measurement. +This option does not scale your acceleration curve in any way. Rather, DPI scales the set of points used to graph your curve, and shows you a window of input speed relevant for your chosen DPI. The poll rate is used as a safeguard for the Last Mouse Move points and therefore should be set for accuracy in that measurement. ## Acceleration Styles The examples of various types below show some typical settings, without a cap or offset, for a mouse at 1200 DPI and 1000 hz. diff --git a/doc/images/anisotropy_example.png b/doc/images/anisotropy_example.png Binary files differnew file mode 100644 index 0000000..6425e68 --- /dev/null +++ b/doc/images/anisotropy_example.png diff --git a/grapher/Constants/Constants.cs b/grapher/Constants/Constants.cs index 5bee626..d58c5cf 100644 --- a/grapher/Constants/Constants.cs +++ b/grapher/Constants/Constants.cs @@ -14,13 +14,10 @@ namespace grapher public const int DefaultPollRate = 1000; /// <summary> Resolution of chart calulation. </summary> - public const int Resolution = 100; + public const int Resolution = 500; /// <summary> Multiplied by DPI over poll rate to find rough max expected velocity. </summary> - public const double MaxMultiplier = 85; - - /// <summary> Ratio of max (X, Y) used in "by component" calulations to those used in "whole vector" calculations. </summary> - public const double XYToCombinedRatio = 1.4; + public const double MaxMultiplier = .05; /// <summary> Separation between X and Y active value labels, in pixels. </summary> public const int ActiveLabelXYSeparation = 2; @@ -46,6 +43,12 @@ namespace grapher /// <summary> Horizontal separation between left side of single dropdown and left side of labels beneath dropdown </summary> public const int DropDownLeftSeparation = 10; + /// <summary> Height of sensitivity chart when displayed alone. </summary> + public const int SensitivityChartAloneHeight = 450; + + /// <summary> Height of sensitivity chart when displayed alongside Velocity and Gain charts. </summary> + public const int SensitivityChartTogetherHeight = 328; + /// <summary> Width of charts when widened </summary> public const int WideChartWidth = 723; @@ -61,8 +64,14 @@ namespace grapher /// <summary> Vertical placement of write button above bottom of sensitivity graph </summary> public const int ButtonVerticalOffset = 60; + /// <summary> Padding between directionality title and containing panel </summary> + public const int DirectionalityTitlePad = 8; + public const float SmallButtonSizeFactor = 0.666f; + /// <summary> Number of divisions between 0 and 90 degrees for directional lookup. For 19: 0, 5, 10... 85, 90.</summary> + public const int AngleDivisions = 19; + /// <summary> Format string for shortened x and y textboxes. </summary> public const string ShortenedFormatString = "0.###"; @@ -111,6 +120,12 @@ namespace grapher /// <summary> Default name of settings file. </summary> public const string DefaultSettingsFileName = @"settings.json"; + /// <summary> Text to direcitonality panel title when panel is closed. </summary> + public const string DirectionalityTitleClosed = "Anisotropy \u25BC"; + + /// <summary> Text to direcitonality panel title when panel is open. </summary> + public const string DirectionalityTitleOpen = "Anisotropy \u25B2"; + /// <summary> Style used by System.Double.Parse </summary> public const NumberStyles FloatStyle = NumberStyles.Float | NumberStyles.AllowThousands; diff --git a/grapher/Form1.Designer.cs b/grapher/Form1.Designer.cs index b375de1..20af913 100644 --- a/grapher/Form1.Designer.cs +++ b/grapher/Form1.Designer.cs @@ -71,6 +71,27 @@ namespace grapher System.Windows.Forms.DataVisualization.Charting.Title title6 = new System.Windows.Forms.DataVisualization.Charting.Title(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RawAcceleration)); this.optionsPanel = new System.Windows.Forms.Panel(); + this.FakeBox = new System.Windows.Forms.CheckBox(); + this.DirectionalityPanel = new System.Windows.Forms.Panel(); + this.LpNormActiveValue = new System.Windows.Forms.Label(); + this.RangeActiveValueY = new System.Windows.Forms.Label(); + this.RangeActiveValueX = new System.Windows.Forms.Label(); + this.DomainActiveValueY = new System.Windows.Forms.Label(); + this.DomainActiveValueX = new System.Windows.Forms.Label(); + this.DirectionalityActiveValueTitle = new System.Windows.Forms.Label(); + this.ByComponentCheckBox = new System.Windows.Forms.CheckBox(); + this.WholeCheckBox = new System.Windows.Forms.CheckBox(); + this.DirectionalityRangeLabel = new System.Windows.Forms.Label(); + this.DirectionalDomainLabel = new System.Windows.Forms.Label(); + this.LpNormBox = new System.Windows.Forms.TextBox(); + this.LPNormLabel = new System.Windows.Forms.Label(); + this.DirectionalityY = new System.Windows.Forms.Label(); + this.DirectionalityX = new System.Windows.Forms.Label(); + this.RangeBoxY = new System.Windows.Forms.TextBox(); + this.RangeBoxX = new System.Windows.Forms.TextBox(); + this.DomainBoxY = new System.Windows.Forms.TextBox(); + this.DomainBoxX = new System.Windows.Forms.TextBox(); + this.DirectionalityLabel = new System.Windows.Forms.Label(); this.toggleButton = new System.Windows.Forms.CheckBox(); this.scaleLabelY = new System.Windows.Forms.Label(); this.ScaleActiveYLabel = new System.Windows.Forms.Label(); @@ -158,9 +179,6 @@ namespace grapher this.offsetStyleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.gainOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.legacyOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.wholeVectorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.byVectorComponentToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AutoWriteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.UseSpecificDeviceMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.chartsPanel = new System.Windows.Forms.Panel(); @@ -171,6 +189,7 @@ namespace grapher this.VelocityChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.AccelerationChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.optionsPanel.SuspendLayout(); + this.DirectionalityPanel.SuspendLayout(); this.menuStrip1.SuspendLayout(); this.chartsPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.GainChartY)).BeginInit(); @@ -184,6 +203,8 @@ namespace grapher // optionsPanel // this.optionsPanel.AutoSize = true; + this.optionsPanel.Controls.Add(this.FakeBox); + this.optionsPanel.Controls.Add(this.DirectionalityPanel); this.optionsPanel.Controls.Add(this.toggleButton); this.optionsPanel.Controls.Add(this.scaleLabelY); this.optionsPanel.Controls.Add(this.ScaleActiveYLabel); @@ -262,9 +283,211 @@ namespace grapher this.optionsPanel.Size = new System.Drawing.Size(483, 956); this.optionsPanel.TabIndex = 34; // + // FakeBox + // + this.FakeBox.AutoSize = true; + this.FakeBox.Location = new System.Drawing.Point(31, 538); + this.FakeBox.Name = "FakeBox"; + this.FakeBox.Size = new System.Drawing.Size(47, 17); + this.FakeBox.TabIndex = 151; + this.FakeBox.Text = "fake"; + this.FakeBox.UseVisualStyleBackColor = true; + // + // DirectionalityPanel + // + this.DirectionalityPanel.Controls.Add(this.LpNormActiveValue); + this.DirectionalityPanel.Controls.Add(this.RangeActiveValueY); + this.DirectionalityPanel.Controls.Add(this.RangeActiveValueX); + this.DirectionalityPanel.Controls.Add(this.DomainActiveValueY); + this.DirectionalityPanel.Controls.Add(this.DomainActiveValueX); + this.DirectionalityPanel.Controls.Add(this.DirectionalityActiveValueTitle); + this.DirectionalityPanel.Controls.Add(this.ByComponentCheckBox); + this.DirectionalityPanel.Controls.Add(this.WholeCheckBox); + this.DirectionalityPanel.Controls.Add(this.DirectionalityRangeLabel); + this.DirectionalityPanel.Controls.Add(this.DirectionalDomainLabel); + this.DirectionalityPanel.Controls.Add(this.LpNormBox); + this.DirectionalityPanel.Controls.Add(this.LPNormLabel); + this.DirectionalityPanel.Controls.Add(this.DirectionalityY); + this.DirectionalityPanel.Controls.Add(this.DirectionalityX); + this.DirectionalityPanel.Controls.Add(this.RangeBoxY); + this.DirectionalityPanel.Controls.Add(this.RangeBoxX); + this.DirectionalityPanel.Controls.Add(this.DomainBoxY); + this.DirectionalityPanel.Controls.Add(this.DomainBoxX); + this.DirectionalityPanel.Controls.Add(this.DirectionalityLabel); + this.DirectionalityPanel.Location = new System.Drawing.Point(12, 369); + this.DirectionalityPanel.Name = "DirectionalityPanel"; + this.DirectionalityPanel.Size = new System.Drawing.Size(298, 135); + this.DirectionalityPanel.TabIndex = 150; + // + // LpNormActiveValue + // + this.LpNormActiveValue.AutoSize = true; + this.LpNormActiveValue.Location = new System.Drawing.Point(185, 81); + this.LpNormActiveValue.Name = "LpNormActiveValue"; + this.LpNormActiveValue.Size = new System.Drawing.Size(13, 13); + this.LpNormActiveValue.TabIndex = 18; + this.LpNormActiveValue.Text = "0"; + // + // RangeActiveValueY + // + this.RangeActiveValueY.AutoSize = true; + this.RangeActiveValueY.Location = new System.Drawing.Point(204, 55); + this.RangeActiveValueY.Name = "RangeActiveValueY"; + this.RangeActiveValueY.Size = new System.Drawing.Size(13, 13); + this.RangeActiveValueY.TabIndex = 17; + this.RangeActiveValueY.Text = "0"; + // + // RangeActiveValueX + // + this.RangeActiveValueX.AutoSize = true; + this.RangeActiveValueX.Location = new System.Drawing.Point(177, 55); + this.RangeActiveValueX.Name = "RangeActiveValueX"; + this.RangeActiveValueX.Size = new System.Drawing.Size(13, 13); + this.RangeActiveValueX.TabIndex = 16; + this.RangeActiveValueX.Text = "0"; + // + // DomainActiveValueY + // + this.DomainActiveValueY.AutoSize = true; + this.DomainActiveValueY.Location = new System.Drawing.Point(204, 28); + this.DomainActiveValueY.Name = "DomainActiveValueY"; + this.DomainActiveValueY.Size = new System.Drawing.Size(13, 13); + this.DomainActiveValueY.TabIndex = 15; + this.DomainActiveValueY.Text = "0"; + // + // DomainActiveValueX + // + this.DomainActiveValueX.AutoSize = true; + this.DomainActiveValueX.Location = new System.Drawing.Point(177, 28); + this.DomainActiveValueX.Name = "DomainActiveValueX"; + this.DomainActiveValueX.Size = new System.Drawing.Size(13, 13); + this.DomainActiveValueX.TabIndex = 14; + this.DomainActiveValueX.Text = "0"; + // + // DirectionalityActiveValueTitle + // + this.DirectionalityActiveValueTitle.AutoSize = true; + this.DirectionalityActiveValueTitle.Location = new System.Drawing.Point(176, 9); + this.DirectionalityActiveValueTitle.Name = "DirectionalityActiveValueTitle"; + this.DirectionalityActiveValueTitle.Size = new System.Drawing.Size(41, 13); + this.DirectionalityActiveValueTitle.TabIndex = 13; + this.DirectionalityActiveValueTitle.Text = "Current"; + // + // ByComponentCheckBox + // + this.ByComponentCheckBox.AutoCheck = false; + this.ByComponentCheckBox.AutoSize = true; + this.ByComponentCheckBox.Location = new System.Drawing.Point(180, 104); + this.ByComponentCheckBox.Name = "ByComponentCheckBox"; + this.ByComponentCheckBox.Size = new System.Drawing.Size(95, 17); + this.ByComponentCheckBox.TabIndex = 12; + this.ByComponentCheckBox.Text = "By Component"; + this.ByComponentCheckBox.UseVisualStyleBackColor = true; + // + // WholeCheckBox + // + this.WholeCheckBox.AutoCheck = false; + this.WholeCheckBox.AutoSize = true; + this.WholeCheckBox.Location = new System.Drawing.Point(43, 104); + this.WholeCheckBox.Name = "WholeCheckBox"; + this.WholeCheckBox.Size = new System.Drawing.Size(57, 17); + this.WholeCheckBox.TabIndex = 11; + this.WholeCheckBox.Text = "Whole"; + this.WholeCheckBox.UseVisualStyleBackColor = true; + // + // DirectionalityRangeLabel + // + this.DirectionalityRangeLabel.AutoSize = true; + this.DirectionalityRangeLabel.Location = new System.Drawing.Point(24, 55); + this.DirectionalityRangeLabel.Name = "DirectionalityRangeLabel"; + this.DirectionalityRangeLabel.Size = new System.Drawing.Size(39, 13); + this.DirectionalityRangeLabel.TabIndex = 10; + this.DirectionalityRangeLabel.Text = "Range"; + // + // DirectionalDomainLabel + // + this.DirectionalDomainLabel.AutoSize = true; + this.DirectionalDomainLabel.Location = new System.Drawing.Point(23, 28); + this.DirectionalDomainLabel.Name = "DirectionalDomainLabel"; + this.DirectionalDomainLabel.Size = new System.Drawing.Size(43, 13); + this.DirectionalDomainLabel.TabIndex = 9; + this.DirectionalDomainLabel.Text = "Domain"; + // + // LpNormBox + // + this.LpNormBox.Location = new System.Drawing.Point(94, 78); + this.LpNormBox.Name = "LpNormBox"; + this.LpNormBox.Size = new System.Drawing.Size(76, 20); + this.LpNormBox.TabIndex = 8; + // + // LPNormLabel + // + this.LPNormLabel.AutoSize = true; + this.LPNormLabel.Location = new System.Drawing.Point(23, 81); + this.LPNormLabel.Name = "LPNormLabel"; + this.LPNormLabel.Size = new System.Drawing.Size(47, 13); + this.LPNormLabel.TabIndex = 7; + this.LPNormLabel.Text = "Lp Norm"; + // + // DirectionalityY + // + this.DirectionalityY.AutoSize = true; + this.DirectionalityY.Location = new System.Drawing.Point(147, 9); + this.DirectionalityY.Name = "DirectionalityY"; + this.DirectionalityY.Size = new System.Drawing.Size(14, 13); + this.DirectionalityY.TabIndex = 6; + this.DirectionalityY.Text = "Y"; + // + // DirectionalityX + // + this.DirectionalityX.AutoSize = true; + this.DirectionalityX.Location = new System.Drawing.Point(103, 9); + this.DirectionalityX.Name = "DirectionalityX"; + this.DirectionalityX.Size = new System.Drawing.Size(14, 13); + this.DirectionalityX.TabIndex = 5; + this.DirectionalityX.Text = "X"; + // + // RangeBoxY + // + this.RangeBoxY.Location = new System.Drawing.Point(136, 52); + this.RangeBoxY.Name = "RangeBoxY"; + this.RangeBoxY.Size = new System.Drawing.Size(34, 20); + this.RangeBoxY.TabIndex = 4; + // + // RangeBoxX + // + this.RangeBoxX.Location = new System.Drawing.Point(94, 52); + this.RangeBoxX.Name = "RangeBoxX"; + this.RangeBoxX.Size = new System.Drawing.Size(34, 20); + this.RangeBoxX.TabIndex = 3; + // + // DomainBoxY + // + this.DomainBoxY.Location = new System.Drawing.Point(136, 25); + this.DomainBoxY.Name = "DomainBoxY"; + this.DomainBoxY.Size = new System.Drawing.Size(34, 20); + this.DomainBoxY.TabIndex = 2; + // + // DomainBoxX + // + this.DomainBoxX.Location = new System.Drawing.Point(94, 25); + this.DomainBoxX.Name = "DomainBoxX"; + this.DomainBoxX.Size = new System.Drawing.Size(34, 20); + this.DomainBoxX.TabIndex = 1; + // + // DirectionalityLabel + // + this.DirectionalityLabel.AutoSize = true; + this.DirectionalityLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.DirectionalityLabel.Location = new System.Drawing.Point(3, 9); + this.DirectionalityLabel.Name = "DirectionalityLabel"; + this.DirectionalityLabel.Size = new System.Drawing.Size(67, 13); + this.DirectionalityLabel.TabIndex = 0; + this.DirectionalityLabel.Text = "Directionality"; + // // toggleButton // - this.toggleButton.Location = new System.Drawing.Point(214, 387); + this.toggleButton.Location = new System.Drawing.Point(211, 534); this.toggleButton.Name = "toggleButton"; this.toggleButton.Size = new System.Drawing.Size(104, 24); this.toggleButton.TabIndex = 112; @@ -710,7 +933,7 @@ namespace grapher // writeButton // this.writeButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F); - this.writeButton.Location = new System.Drawing.Point(94, 380); + this.writeButton.Location = new System.Drawing.Point(90, 527); this.writeButton.Name = "writeButton"; this.writeButton.Size = new System.Drawing.Size(92, 35); this.writeButton.TabIndex = 111; @@ -955,7 +1178,6 @@ namespace grapher this.advancedToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.capStyleToolStripMenuItem, this.offsetStyleToolStripMenuItem, - this.toolStripMenuItem1, this.AutoWriteMenuItem, this.UseSpecificDeviceMenuItem}); this.advancedToolStripMenuItem.Name = "advancedToolStripMenuItem"; @@ -1006,30 +1228,6 @@ namespace grapher this.legacyOffsetToolStripMenuItem.Size = new System.Drawing.Size(147, 22); this.legacyOffsetToolStripMenuItem.Text = "Legacy"; // - // toolStripMenuItem1 - // - this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.wholeVectorToolStripMenuItem, - this.byVectorComponentToolStripMenuItem}); - this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(210, 22); - this.toolStripMenuItem1.Text = "Application Style"; - // - // wholeVectorToolStripMenuItem - // - this.wholeVectorToolStripMenuItem.Checked = true; - this.wholeVectorToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; - this.wholeVectorToolStripMenuItem.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; - this.wholeVectorToolStripMenuItem.Name = "wholeVectorToolStripMenuItem"; - this.wholeVectorToolStripMenuItem.Size = new System.Drawing.Size(154, 22); - this.wholeVectorToolStripMenuItem.Text = "Whole"; - // - // byVectorComponentToolStripMenuItem - // - this.byVectorComponentToolStripMenuItem.Name = "byVectorComponentToolStripMenuItem"; - this.byVectorComponentToolStripMenuItem.Size = new System.Drawing.Size(154, 22); - this.byVectorComponentToolStripMenuItem.Text = "By Component"; - // // AutoWriteMenuItem // this.AutoWriteMenuItem.Checked = true; @@ -1309,6 +1507,8 @@ namespace grapher this.Text = "Raw Accel"; this.optionsPanel.ResumeLayout(false); this.optionsPanel.PerformLayout(); + this.DirectionalityPanel.ResumeLayout(false); + this.DirectionalityPanel.PerformLayout(); this.menuStrip1.ResumeLayout(false); this.menuStrip1.PerformLayout(); this.chartsPanel.ResumeLayout(false); @@ -1412,9 +1612,6 @@ namespace grapher private System.Windows.Forms.ToolStripMenuItem offsetStyleToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem gainOffsetToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem legacyOffsetToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; - private System.Windows.Forms.ToolStripMenuItem wholeVectorToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem byVectorComponentToolStripMenuItem; private System.Windows.Forms.Panel chartsPanel; private System.Windows.Forms.DataVisualization.Charting.Chart GainChartY; private System.Windows.Forms.DataVisualization.Charting.Chart VelocityChartY; @@ -1423,6 +1620,27 @@ namespace grapher private System.Windows.Forms.DataVisualization.Charting.Chart VelocityChart; private System.Windows.Forms.DataVisualization.Charting.Chart AccelerationChart; private System.Windows.Forms.ToolStripMenuItem AutoWriteMenuItem; + private System.Windows.Forms.Panel DirectionalityPanel; + private System.Windows.Forms.Label DirectionalityRangeLabel; + private System.Windows.Forms.Label DirectionalDomainLabel; + private System.Windows.Forms.TextBox LpNormBox; + private System.Windows.Forms.Label LPNormLabel; + private System.Windows.Forms.Label DirectionalityY; + private System.Windows.Forms.Label DirectionalityX; + private System.Windows.Forms.TextBox RangeBoxY; + private System.Windows.Forms.TextBox RangeBoxX; + private System.Windows.Forms.TextBox DomainBoxY; + private System.Windows.Forms.TextBox DomainBoxX; + private System.Windows.Forms.Label DirectionalityLabel; + private System.Windows.Forms.CheckBox ByComponentCheckBox; + private System.Windows.Forms.CheckBox WholeCheckBox; + private System.Windows.Forms.Label DirectionalityActiveValueTitle; + private System.Windows.Forms.Label DomainActiveValueX; + private System.Windows.Forms.Label DomainActiveValueY; + private System.Windows.Forms.Label RangeActiveValueX; + private System.Windows.Forms.Label LpNormActiveValue; + private System.Windows.Forms.Label RangeActiveValueY; + private System.Windows.Forms.CheckBox FakeBox; private System.Windows.Forms.ToolStripMenuItem UseSpecificDeviceMenuItem; } } diff --git a/grapher/Form1.cs b/grapher/Form1.cs index afa7a50..71a5e01 100644 --- a/grapher/Form1.cs +++ b/grapher/Form1.cs @@ -64,8 +64,6 @@ namespace grapher toggleButton, showVelocityGainToolStripMenuItem, showLastMouseMoveToolStripMenuItem, - wholeVectorToolStripMenuItem, - byVectorComponentToolStripMenuItem, gainCapToolStripMenuItem, legacyCapToolStripMenuItem, gainOffsetToolStripMenuItem, @@ -75,6 +73,7 @@ namespace grapher ScaleMenuItem, DPITextBox, PollRateTextBox, + DirectionalityPanel, sensitivityBoxX, sensitivityBoxY, rotationBox, @@ -94,8 +93,16 @@ namespace grapher expBoxY, midpointBoxX, midpointBoxY, + DomainBoxX, + DomainBoxY, + RangeBoxX, + RangeBoxY, + LpNormBox, sensXYLock, ByComponentXYLock, + FakeBox, + WholeCheckBox, + ByComponentCheckBox, LockXYLabel, sensitivityLabel, rotationLabel, @@ -140,7 +147,19 @@ namespace grapher AccelTypeActiveLabelY, OptionSetXTitle, OptionSetYTitle, - MouseLabel); + MouseLabel, + DirectionalityLabel, + DirectionalityX, + DirectionalityY, + DirectionalityActiveValueTitle, + LPNormLabel, + LpNormActiveValue, + DirectionalDomainLabel, + DomainActiveValueX, + DomainActiveValueY, + DirectionalityRangeLabel, + RangeActiveValueX, + RangeActiveValueY); } #endregion Constructor diff --git a/grapher/Layouts/LayoutBase.cs b/grapher/Layouts/LayoutBase.cs index 7ed08ef..83af292 100644 --- a/grapher/Layouts/LayoutBase.cs +++ b/grapher/Layouts/LayoutBase.cs @@ -64,7 +64,6 @@ namespace grapher.Layouts IOption limitOption, IOption expOption, IOption midpointOption, - Button button, int top) { @@ -106,8 +105,7 @@ namespace grapher.Layouts IOption offsetOption, IOption limitOption, IOption expOption, - IOption midpointOption, - Button button) + IOption midpointOption) { Layout(accelOption, scaleOption, @@ -117,7 +115,6 @@ namespace grapher.Layouts limitOption, expOption, midpointOption, - button, accelOption.Top); } } diff --git a/grapher/Layouts/PowerLayout.cs b/grapher/Layouts/PowerLayout.cs index 1911813..da7d5bb 100644 --- a/grapher/Layouts/PowerLayout.cs +++ b/grapher/Layouts/PowerLayout.cs @@ -15,7 +15,7 @@ namespace grapher.Layouts ScaleLayout = new OptionLayout(true, Scale); CapLayout = new OptionLayout(true, Cap); WeightLayout = new OptionLayout(true, Weight); - OffsetLayout = new OptionLayout(true, Offset); + OffsetLayout = new OptionLayout(false, string.Empty); LimitLayout = new OptionLayout(false, string.Empty); ExponentLayout = new OptionLayout(true, Exponent); MidpointLayout = new OptionLayout(false, string.Empty); diff --git a/grapher/Models/AccelGUI.cs b/grapher/Models/AccelGUI.cs index bb634ff..5cd7012 100644 --- a/grapher/Models/AccelGUI.cs +++ b/grapher/Models/AccelGUI.cs @@ -72,6 +72,11 @@ namespace grapher SetupButtons(); AccelForm.DoResize(); + + // TODO: The below removes an overlapping form from the anisotropy panel. + // Figure out why and remove the overlap and below. + ApplyOptions.Directionality.Show(); + ApplyOptions.Directionality.Hide(); } #endregion Constructors @@ -149,6 +154,8 @@ namespace grapher args = newArgs, minimumTime = driverSettings.minimumTime, directionalMultipliers = driverSettings.directionalMultipliers, + domainArgs = ApplyOptions.Directionality.GetDomainArgs(), + rangeXY = ApplyOptions.Directionality.GetRangeXY(), deviceID = DeviceIDManager.ID, }; @@ -206,7 +213,7 @@ namespace grapher private void SetupButtons() { - WriteButton.Top = AccelCharts.Top + AccelCharts.TopChartHeight - Constants.ButtonVerticalOffset; + WriteButton.Top = Constants.SensitivityChartAloneHeight - Constants.ButtonVerticalOffset; ToggleButton.Appearance = Appearance.Button; ToggleButton.FlatStyle = FlatStyle.System; diff --git a/grapher/Models/AccelGUIFactory.cs b/grapher/Models/AccelGUIFactory.cs index 0669bf5..6a4c46f 100644 --- a/grapher/Models/AccelGUIFactory.cs +++ b/grapher/Models/AccelGUIFactory.cs @@ -2,6 +2,7 @@ using grapher.Models.Devices; using grapher.Models.Mouse; using grapher.Models.Options; +using grapher.Models.Options.Directionality; using grapher.Models.Serialized; using System; using System.Windows.Forms; @@ -28,8 +29,6 @@ namespace grapher.Models ButtonBase toggleButton, ToolStripMenuItem showVelocityGainToolStripMenuItem, ToolStripMenuItem showLastMouseMoveMenuItem, - ToolStripMenuItem wholeVectorToolStripMenuItem, - ToolStripMenuItem byVectorComponentToolStripMenuItem, ToolStripMenuItem velocityGainCapToolStripMenuItem, ToolStripMenuItem legacyCapToolStripMenuItem, ToolStripMenuItem gainOffsetToolStripMenuItem, @@ -39,6 +38,7 @@ namespace grapher.Models ToolStripMenuItem scaleMenuItem, ToolStripTextBox dpiTextBox, ToolStripTextBox pollRateTextBox, + Panel directionalityPanel, TextBox sensitivityBoxX, TextBox sensitivityBoxY, TextBox rotationBox, @@ -58,8 +58,16 @@ namespace grapher.Models TextBox expBoxY, TextBox midpointBoxX, TextBox midpointBoxY, + TextBox domainBoxX, + TextBox domainBoxY, + TextBox rangeBoxX, + TextBox rangeBoxY, + TextBox lpNormBox, CheckBox sensXYLock, CheckBox byComponentXYLock, + CheckBox fakeBox, + CheckBox wholeCheckBox, + CheckBox byComponentCheckBox, Label lockXYLabel, Label sensitivityLabel, Label rotationLabel, @@ -104,8 +112,23 @@ namespace grapher.Models Label accelTypeActiveLabelY, Label optionSetXTitle, Label optionSetYTitle, - Label mouseLabel) + Label mouseLabel, + Label directionalityLabel, + Label directionalityX, + Label directionalityY, + Label direcionalityActiveValueTitle, + Label lpNormLabel, + Label lpNormActiveLabel, + Label domainLabel, + Label domainActiveValueX, + Label domainActiveValueY, + Label rangeLabel, + Label rangeActiveValueX, + Label rangeActiveValueY) { + fakeBox.Checked = false; + fakeBox.Hide(); + var accelCalculator = new AccelCalculator( new Field(dpiTextBox.TextBox, form, Constants.DefaultDPI, 1), new Field(pollRateTextBox.TextBox, form, Constants.DefaultPollRate, 1)); @@ -143,6 +166,8 @@ namespace grapher.Models var optionSetYLeft = rotation.Left + rotation.Width; + var directionalityLeft = directionalityPanel.Left; + var weightX = new Option( weightBoxX, form, @@ -267,6 +292,36 @@ namespace grapher.Models new ActiveValueLabel(midpointActiveLabelY, activeValueTitleY), optionSetYLeft); + var lpNorm = new Option( + new Field(lpNormBox, form, 2), + lpNormLabel, + new ActiveValueLabel(lpNormActiveLabel, direcionalityActiveValueTitle), + directionalityLeft); + + var domain = new OptionXY( + domainBoxX, + domainBoxY, + fakeBox, + form, + 1, + domainLabel, + new ActiveValueLabelXY( + new ActiveValueLabel(domainActiveValueX, direcionalityActiveValueTitle), + new ActiveValueLabel(domainActiveValueY, direcionalityActiveValueTitle)), + false); + + var range = new OptionXY( + rangeBoxX, + rangeBoxY, + fakeBox, + form, + 1, + rangeLabel, + new ActiveValueLabelXY( + new ActiveValueLabel(rangeActiveValueX, direcionalityActiveValueTitle), + new ActiveValueLabel(rangeActiveValueY, direcionalityActiveValueTitle)), + false); + var capOptionsX = new CapOptions( velocityGainCapToolStripMenuItem, legacyCapToolStripMenuItem, @@ -315,12 +370,24 @@ namespace grapher.Models rotationBox.Top + rotationBox.Height + Constants.OptionVerticalSeperation, accelerationOptionsY); + var directionalOptions = new DirectionalityOptions( + directionalityPanel, + directionalityLabel, + directionalityX, + directionalityY, + direcionalityActiveValueTitle, + lpNorm, + domain, + range, + wholeCheckBox, + byComponentCheckBox, + 245); + var applyOptions = new ApplyOptions( - wholeVectorToolStripMenuItem, - byVectorComponentToolStripMenuItem, byComponentXYLock, optionsSetX, optionsSetY, + directionalOptions, sensitivity, rotation, lockXYLabel, @@ -339,7 +406,6 @@ namespace grapher.Models var mouseWatcher = new MouseWatcher(form, mouseLabel, accelCharts, settings); - return new AccelGUI( form, accelCalculator, diff --git a/grapher/Models/Calculations/AccelCalculator.cs b/grapher/Models/Calculations/AccelCalculator.cs index 2c32753..42b7347 100644 --- a/grapher/Models/Calculations/AccelCalculator.cs +++ b/grapher/Models/Calculations/AccelCalculator.cs @@ -14,12 +14,24 @@ namespace grapher.Models.Calculations { public double velocity; public double time; + public double angle; public int x; public int y; } #endregion Structs + #region Static + + public static double[] SlowMovements = + { + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.333, 3.666, 4.0, 4.333, 4.666, + }; + + public IEnumerable<double> Angles = GetAngles(); + + #endregion static + #region Constructors public AccelCalculator(Field dpi, Field pollRate) @@ -38,15 +50,15 @@ namespace grapher.Models.Calculations public ReadOnlyCollection<SimulatedMouseInput> SimulatedInputY { get; private set; } + public IReadOnlyCollection<IReadOnlyCollection<SimulatedMouseInput>> SimulatedDirectionalInput { get; private set; } + public Field DPI { get; private set; } public Field PollRate { get; private set; } - private double CombinedMaxVelocity { get; set; } - - private double XYMaxVelocity { get; set; } + private double MaxVelocity { get; set; } - private int Increment { get; set; } + private double Increment { get; set; } private double MeasurementTime { get; set; } @@ -58,10 +70,26 @@ namespace grapher.Models.Calculations #region Methods + public static IEnumerable<double> GetAngles() + { + for(double i=0; i < (Constants.AngleDivisions); i++) + { + yield return (i / (Constants.AngleDivisions-1.0)) * (Math.PI / 2); + } + } + + public static int NearestAngleDivision(double angle) + { + var angleTransformed = angle * 2 / Math.PI * (Constants.AngleDivisions-1); + return (int)Math.Round(angleTransformed); + } + public void Calculate(AccelChartData data, ManagedAccel accel, double starter, ICollection<SimulatedMouseInput> simulatedInputData) { double lastInputMagnitude = 0; double lastOutputMagnitude = 0; + SimulatedMouseInput lastInput; + double lastSlope = 0; double maxRatio = 0.0; double minRatio = Double.MaxValue; @@ -81,6 +109,13 @@ namespace grapher.Models.Calculations var output = accel.Accelerate(simulatedInputDatum.x, simulatedInputDatum.y, simulatedInputDatum.time); var outMagnitude = Velocity(output.Item1, output.Item2, simulatedInputDatum.time); + var inDiff = Math.Round(simulatedInputDatum.velocity - lastInputMagnitude, 5); + var outDiff = Math.Round(outMagnitude - lastOutputMagnitude, 5); + + if (inDiff == 0) + { + continue; + } if (!data.VelocityPoints.ContainsKey(simulatedInputDatum.velocity)) { @@ -99,7 +134,8 @@ namespace grapher.Models.Calculations } var ratio = outMagnitude / simulatedInputDatum.velocity; - + var slope = inDiff > 0 ? outDiff / inDiff : starter; + if (ratio > maxRatio) { maxRatio = ratio; @@ -110,10 +146,6 @@ namespace grapher.Models.Calculations minRatio = ratio; } - var inDiff = simulatedInputDatum.velocity - lastInputMagnitude; - var outDiff = outMagnitude - lastOutputMagnitude; - var slope = inDiff > 0 ? outDiff / inDiff : starter; - if (slope > maxSlope) { maxSlope = slope; @@ -137,6 +169,8 @@ namespace grapher.Models.Calculations lastInputMagnitude = simulatedInputDatum.velocity; lastOutputMagnitude = outMagnitude; index += 1; + lastInput = simulatedInputDatum; + lastSlope = slope; } index--; @@ -154,12 +188,8 @@ namespace grapher.Models.Calculations data.MinGain = minSlope; } - public void CalculateCombinedDiffSens(AccelData data, ManagedAccel accel, DriverSettings settings, ICollection<SimulatedMouseInput> simulatedInputData) + public void CalculateDirectional(AccelChartData[] dataByAngle, ManagedAccel accel, DriverSettings settings, IReadOnlyCollection<IReadOnlyCollection<SimulatedMouseInput>> simulatedInputData) { - double lastInputMagnitude = 0; - double lastOutputMagnitudeX = 0; - double lastOutputMagnitudeY = 0; - double maxRatio = 0.0; double minRatio = Double.MaxValue; double maxSlope = 0.0; @@ -168,156 +198,143 @@ namespace grapher.Models.Calculations Sensitivity = GetSens(ref settings); - double log = -2; - int index = 0; - int logIndex = 0; + int angleIndex = 0; - foreach (var simulatedInputDatum in simulatedInputData) + foreach (var simulatedInputDataAngle in simulatedInputData) { - if (simulatedInputDatum.velocity <= 0) - { - continue; - } - - var output = accel.Accelerate(simulatedInputDatum.x, simulatedInputDatum.y, simulatedInputDatum.time); - var outputWithoutSens = StripThisSens(output.Item1, output.Item2); - var magnitudeWithoutSens = Velocity(outputWithoutSens.Item1, outputWithoutSens.Item2, simulatedInputDatum.time); + double log = -2; + int index = 0; + int logIndex = 0; + double lastInputMagnitude = 0; + double lastOutputMagnitude = 0; - var ratio = magnitudeWithoutSens / simulatedInputDatum.velocity; + var data = dataByAngle[angleIndex]; - if (!data.Combined.VelocityPoints.ContainsKey(simulatedInputDatum.velocity)) + foreach (var simulatedInputDatum in simulatedInputDataAngle) { - data.Combined.VelocityPoints.Add(simulatedInputDatum.velocity, magnitudeWithoutSens); - } - else - { - continue; + if (simulatedInputDatum.velocity <= 0) + { + continue; + } + + var output = accel.Accelerate(simulatedInputDatum.x, simulatedInputDatum.y, simulatedInputDatum.time); + var magnitude = Velocity(output.Item1, output.Item2, simulatedInputDatum.time); + var inDiff = Math.Round(simulatedInputDatum.velocity - lastInputMagnitude, 5); + var outDiff = Math.Round(magnitude - lastOutputMagnitude, 5); + + if (inDiff == 0) + { + continue; + } + + if (!data.VelocityPoints.ContainsKey(simulatedInputDatum.velocity)) + { + data.VelocityPoints.Add(simulatedInputDatum.velocity, magnitude); + } + else + { + continue; + } + + while (Math.Pow(10, log) < magnitude && logIndex < data.LogToIndex.Length) + { + data.LogToIndex[logIndex] = index; + log += 0.01; + logIndex++; + } + + var ratio = magnitude / simulatedInputDatum.velocity; + var slope = inDiff > 0 ? outDiff / inDiff : settings.sensitivity.x; + + bool indexToMeasureExtrema = (angleIndex == 0) || (angleIndex == (Constants.AngleDivisions - 1)); + + if (indexToMeasureExtrema && (ratio > maxRatio)) + { + maxRatio = ratio; + } + + if (indexToMeasureExtrema && (ratio < minRatio)) + { + minRatio = ratio; + } + + if (indexToMeasureExtrema && (slope > maxSlope)) + { + maxSlope = slope; + } + + if (indexToMeasureExtrema && (slope < minSlope)) + { + minSlope = slope; + } + + if (!data.AccelPoints.ContainsKey(simulatedInputDatum.velocity)) + { + data.AccelPoints.Add(simulatedInputDatum.velocity, ratio); + } + + if (!data.GainPoints.ContainsKey(simulatedInputDatum.velocity)) + { + data.GainPoints.Add(simulatedInputDatum.velocity, slope); + } + + lastInputMagnitude = simulatedInputDatum.velocity; + lastOutputMagnitude = magnitude; + index += 1; } - while (Math.Pow(10,log) < magnitudeWithoutSens && logIndex < data.Combined.LogToIndex.Length) + index--; + + while (log <= 5.0) { - data.Combined.LogToIndex[logIndex] = index; + data.LogToIndex[logIndex] = index; log += 0.01; logIndex++; } - var xRatio = settings.sensitivity.x * ratio; - var yRatio = settings.sensitivity.y * ratio; - - if (xRatio > maxRatio) - { - maxRatio = xRatio; - } - - if (xRatio < minRatio) - { - minRatio = xRatio; - } - - if (yRatio > maxRatio) - { - maxRatio = yRatio; - } - - if (yRatio < minRatio) - { - minRatio = yRatio; - } - - if (!data.X.AccelPoints.ContainsKey(simulatedInputDatum.velocity)) - { - data.X.AccelPoints.Add(simulatedInputDatum.velocity, xRatio); - } - - if (!data.Y.AccelPoints.ContainsKey(simulatedInputDatum.velocity)) - { - data.Y.AccelPoints.Add(simulatedInputDatum.velocity, yRatio); - } - - var xOut = xRatio * simulatedInputDatum.velocity; - var yOut = yRatio * simulatedInputDatum.velocity; - - var inDiff = simulatedInputDatum.velocity - lastInputMagnitude; - var xOutDiff = xOut - lastOutputMagnitudeX; - var yOutDiff = yOut - lastOutputMagnitudeY; - var xSlope = inDiff > 0 ? xOutDiff / inDiff : settings.sensitivity.x; - var ySlope = inDiff > 0 ? yOutDiff / inDiff : settings.sensitivity.y; - - if (xSlope > maxSlope) - { - maxSlope = xSlope; - } - - if (xSlope < minSlope) - { - minSlope = xSlope; - } - - if (ySlope > maxSlope) - { - maxSlope = ySlope; - } - - if (ySlope < minSlope) - { - minSlope = ySlope; - } - - if (!data.X.VelocityPoints.ContainsKey(simulatedInputDatum.velocity)) - { - data.X.VelocityPoints.Add(simulatedInputDatum.velocity, xOut); - } - - if (!data.Y.VelocityPoints.ContainsKey(simulatedInputDatum.velocity)) - { - data.Y.VelocityPoints.Add(simulatedInputDatum.velocity, yOut); - } - - if (!data.X.GainPoints.ContainsKey(simulatedInputDatum.velocity)) - { - data.X.GainPoints.Add(simulatedInputDatum.velocity, xSlope); - } - - if (!data.Y.GainPoints.ContainsKey(simulatedInputDatum.velocity)) - { - data.Y.GainPoints.Add(simulatedInputDatum.velocity, ySlope); - } - - lastInputMagnitude = simulatedInputDatum.velocity; - lastOutputMagnitudeX = xOut; - lastOutputMagnitudeY = yOut; - index += 1; + angleIndex++; } - index--; - - while (log <= 5.0) - { - data.Combined.LogToIndex[logIndex] = index; - log += 0.01; - logIndex++; - } - - data.Combined.MaxAccel = maxRatio; - data.Combined.MinAccel = minRatio; - data.Combined.MaxGain = maxSlope; - data.Combined.MinGain = minSlope; + dataByAngle[0].MaxAccel = maxRatio; + dataByAngle[0].MinAccel = minRatio; + dataByAngle[0].MaxGain = maxSlope; + dataByAngle[0].MinGain = minSlope; } public ReadOnlyCollection<SimulatedMouseInput> GetSimulatedInput() { var magnitudes = new List<SimulatedMouseInput>(); - for (int i = 0; i < CombinedMaxVelocity; i+=Increment) + + foreach (var slowMoveX in SlowMovements) { - for (int j = 0; j <= i; j+=Increment) - { - SimulatedMouseInput mouseInputData; - mouseInputData.x = i; - mouseInputData.y = j; - mouseInputData.time = MeasurementTime; - mouseInputData.velocity = Velocity(i, j, mouseInputData.time); - magnitudes.Add(mouseInputData); - } + var slowMoveY = slowMoveX; + var ratio = slowMoveX > 0.0 ? slowMoveY / slowMoveX : 1; + var ceilX = (int)Math.Round(slowMoveX*50); + var ceilY = (int)Math.Round(slowMoveY*50); + var ceilMagnitude = Magnitude(ceilX, ceilY); + var timeFactor = ceilMagnitude / Magnitude(slowMoveX, slowMoveY); + + SimulatedMouseInput mouseInputData; + mouseInputData.x = ceilX; + mouseInputData.y = ceilY; + mouseInputData.time = MeasurementTime*timeFactor; + mouseInputData.velocity = Velocity(ceilX, ceilY, mouseInputData.time); + mouseInputData.angle = Math.Atan2(ceilY, ceilX); + magnitudes.Add(mouseInputData); + + } + + for (double i = 5; i < MaxVelocity; i+=Increment) + { + SimulatedMouseInput mouseInputData; + var ceil = (int)Math.Ceiling(i); + var timeFactor = ceil / i; + mouseInputData.x = ceil; + mouseInputData.y = 0; + mouseInputData.time = MeasurementTime * timeFactor; + mouseInputData.velocity = Velocity(ceil, 0, mouseInputData.time); + mouseInputData.angle = Math.Atan2(ceil, 0); + magnitudes.Add(mouseInputData); } magnitudes.Sort((m1, m2) => m1.velocity.CompareTo(m2.velocity)); @@ -329,13 +346,29 @@ namespace grapher.Models.Calculations { var magnitudes = new List<SimulatedMouseInput>(); - for (int i = 0; i < XYMaxVelocity; i+=Increment) + foreach (var slowMovement in SlowMovements) { + var ceil = (int)Math.Ceiling(slowMovement); + var timeFactor = ceil / slowMovement; SimulatedMouseInput mouseInputData; - mouseInputData.x = i; + mouseInputData.x = ceil; mouseInputData.y = 0; - mouseInputData.time = MeasurementTime; - mouseInputData.velocity = Velocity(i, 0, mouseInputData.time); + mouseInputData.time = MeasurementTime*timeFactor; + mouseInputData.velocity = Velocity(ceil, 0, mouseInputData.time); + mouseInputData.angle = 0; + magnitudes.Add(mouseInputData); + } + + for (double i = 5; i < MaxVelocity; i+=Increment) + { + SimulatedMouseInput mouseInputData; + var ceil = (int)Math.Ceiling(i); + var timeFactor = ceil / i; + mouseInputData.x = ceil; + mouseInputData.y = 0; + mouseInputData.time = MeasurementTime*timeFactor; + mouseInputData.velocity = Velocity(ceil, 0, mouseInputData.time); + mouseInputData.angle = 0; magnitudes.Add(mouseInputData); } @@ -346,19 +379,86 @@ namespace grapher.Models.Calculations { var magnitudes = new List<SimulatedMouseInput>(); - for (int i = 0; i < XYMaxVelocity; i+=Increment) + foreach (var slowMovement in SlowMovements) + { + var ceil = (int)Math.Ceiling(slowMovement); + var timeFactor = ceil / slowMovement; + SimulatedMouseInput mouseInputData; + mouseInputData.x = 0; + mouseInputData.y = ceil; + mouseInputData.time = MeasurementTime*timeFactor; + mouseInputData.velocity = Velocity(0, ceil, mouseInputData.time); + mouseInputData.angle = 0; + magnitudes.Add(mouseInputData); + } + + for (double i = 5; i < MaxVelocity; i+=Increment) { SimulatedMouseInput mouseInputData; + var ceil = (int)Math.Ceiling(i); + var timeFactor = ceil / i; mouseInputData.x = 0; - mouseInputData.y = i; + mouseInputData.y = ceil; mouseInputData.time = MeasurementTime; mouseInputData.velocity = Velocity(0, i, mouseInputData.time); + mouseInputData.angle = Math.PI / 2; magnitudes.Add(mouseInputData); } return magnitudes.AsReadOnly(); } + public IReadOnlyCollection<IReadOnlyCollection<SimulatedMouseInput>> GetSimulatedDirectionalInput() + { + var magnitudesByAngle = new List<IReadOnlyCollection<SimulatedMouseInput>>(); + + foreach (var angle in Angles) + { + var magnitudes = new List<SimulatedMouseInput>(); + + foreach (var slowMoveMagnitude in SlowMovements) + { + var slowMoveX = Math.Round(slowMoveMagnitude * Math.Cos(angle), 4); + var slowMoveY = Math.Round(slowMoveMagnitude * Math.Sin(angle), 4); + var ceilX = (int)Math.Round(slowMoveX*90); + var ceilY = (int)Math.Round(slowMoveY*90); + var ceilMagnitude = Magnitude(ceilX, ceilY); + var timeFactor = ceilMagnitude / slowMoveMagnitude; + + SimulatedMouseInput mouseInputData; + mouseInputData.x = ceilX; + mouseInputData.y = ceilY; + mouseInputData.time = timeFactor; + mouseInputData.velocity = Velocity(ceilX, ceilY, timeFactor); + mouseInputData.angle = angle; + magnitudes.Add(mouseInputData); + } + + for (double magnitude = 5; magnitude < MaxVelocity; magnitude+=Increment) + { + var slowMoveX = Math.Round(magnitude * Math.Cos(angle), 4); + var slowMoveY = Math.Round(magnitude * Math.Sin(angle), 4); + var ratio = slowMoveX > 0.0 ? slowMoveY / slowMoveX : 90; + var ceilX = (int)Math.Round(slowMoveX*90); + var ceilY = (int)Math.Round(slowMoveY*ratio); + var ceilMagnitude = Magnitude(ceilX, ceilY); + var timeFactor = ceilMagnitude / magnitude; + + SimulatedMouseInput mouseInputData; + mouseInputData.x = ceilX; + mouseInputData.y = ceilY; + mouseInputData.time = timeFactor; + mouseInputData.velocity = Velocity(ceilX, ceilY, mouseInputData.time); + mouseInputData.angle = angle; + magnitudes.Add(mouseInputData); + } + + magnitudesByAngle.Add(magnitudes.AsReadOnly()); + } + + return magnitudesByAngle.AsReadOnly(); + } + public static double Magnitude(int x, int y) { return Math.Sqrt(x * x + y * y); @@ -405,15 +505,14 @@ namespace grapher.Models.Calculations public void ScaleByMouseSettings() { - var dpiPollFactor = DPI.Data / PollRate.Data; - CombinedMaxVelocity = dpiPollFactor * Constants.MaxMultiplier; - var ratio = CombinedMaxVelocity / Constants.Resolution; - Increment = ratio > 1 ? (int) Math.Floor(ratio) : 1; - MeasurementTime = Increment == 1 ? 1 / ratio : 1; - XYMaxVelocity = CombinedMaxVelocity * Constants.XYToCombinedRatio; + MaxVelocity = DPI.Data * Constants.MaxMultiplier; + var ratio = MaxVelocity / Constants.Resolution; + Increment = ratio; + MeasurementTime = 1; SimulatedInputCombined = GetSimulatedInput(); SimulatedInputX = GetSimulatInputX(); SimulatedInputY = GetSimulatedInputY(); + SimulatedDirectionalInput = GetSimulatedDirectionalInput(); } #endregion Methods diff --git a/grapher/Models/Calculations/AccelData.cs b/grapher/Models/Calculations/AccelData.cs deleted file mode 100644 index d35217f..0000000 --- a/grapher/Models/Calculations/AccelData.cs +++ /dev/null @@ -1,117 +0,0 @@ -using grapher.Models.Charts; -using grapher.Models.Serialized; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace grapher.Models.Calculations -{ - public class AccelData - { - #region Constructors - - public AccelData( - EstimatedPoints combined, - EstimatedPoints x, - EstimatedPoints y) - { - Combined = new AccelChartData(); - X = new AccelChartData(); - Y = new AccelChartData(); - - Estimated = combined; - EstimatedX = x; - EstimatedY = y; - - OutVelocityToPoints = new Dictionary<double, (double, double, double, double, double, double, double)>(); - } - - #endregion Constructors - - #region Properties - - public AccelChartData Combined { get; } - - public AccelChartData X { get; } - - public AccelChartData Y { get; } - - private EstimatedPoints Estimated { get; } - - private EstimatedPoints EstimatedX { get; } - - private EstimatedPoints EstimatedY { get; } - - private Dictionary<double, (double, double, double, double, double, double, double)> OutVelocityToPoints { get; } - - #endregion Properties - - #region Methods - - public void Clear() - { - Combined.Clear(); - X.Clear(); - Y.Clear(); - OutVelocityToPoints.Clear(); - } - - public void CalculateDots(double x, double y, double timeInMs) - { - var outVelocity = AccelCalculator.Velocity(x, y, timeInMs); - - (var inCombVel, var combSens, var combGain) = Combined.FindPointValuesFromOut(outVelocity); - Estimated.Velocity.Set(inCombVel, outVelocity); - Estimated.Sensitivity.Set(inCombVel, combSens); - Estimated.Gain.Set(inCombVel, combGain); - } - - public void CalculateDotsXY(double x, double y, double timeInMs) - { - var outX = Math.Abs(x) / timeInMs; - var outY = Math.Abs(y) / timeInMs; - - (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); - } - - public void CalculateDotsCombinedDiffSens(double x, double y, double timeInMs, DriverSettings settings) - { - (var xStripped, var yStripped) = AccelCalculator.StripSens(x, y, settings.sensitivity.x, settings.sensitivity.y); - var outVelocity = AccelCalculator.Velocity(xStripped, yStripped, timeInMs); - - if (OutVelocityToPoints.TryGetValue(outVelocity, out var points)) - { - EstimatedX.Sensitivity.Set(points.Item1, points.Item2); - EstimatedX.Velocity.Set(points.Item1, points.Item3); - EstimatedX.Gain.Set(points.Item1, points.Item4); - EstimatedY.Sensitivity.Set(points.Item1, points.Item5); - EstimatedY.Velocity.Set(points.Item1, points.Item6); - EstimatedY.Gain.Set(points.Item1, points.Item7); - } - else - { - var index = Combined.GetVelocityIndex(outVelocity); - var inVelocity = Combined.VelocityPoints.ElementAt(index).Key; - var xPoints = X.ValuesAtIndex(index); - var yPoints = Y.ValuesAtIndex(index); - OutVelocityToPoints.Add(outVelocity, (inVelocity, xPoints.Item1, xPoints.Item2, xPoints.Item3, yPoints.Item1, yPoints.Item2, yPoints.Item3)); - EstimatedX.Sensitivity.Set(inVelocity, xPoints.Item1); - EstimatedX.Velocity.Set(inVelocity, xPoints.Item2); - EstimatedX.Gain.Set(inVelocity, xPoints.Item3); - EstimatedY.Sensitivity.Set(inVelocity, yPoints.Item1); - EstimatedY.Velocity.Set(inVelocity, yPoints.Item2); - EstimatedY.Gain.Set(inVelocity, yPoints.Item3); - } - } - - #endregion Methods - } -} diff --git a/grapher/Models/Calculations/Data/AccelDataCombined.cs b/grapher/Models/Calculations/Data/AccelDataCombined.cs new file mode 100644 index 0000000..8efb9ac --- /dev/null +++ b/grapher/Models/Calculations/Data/AccelDataCombined.cs @@ -0,0 +1,49 @@ +using grapher.Models.Charts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace grapher.Models.Calculations.Data +{ + public class AccelDataCombined : IAccelData + { + public AccelDataCombined(EstimatedPoints points, AccelCalculator calculator) + { + X = new AccelChartData(); + Points = points; + Calculator = calculator; + } + + public AccelChartData X { get; } + + public AccelChartData Y { get => X; } + + private EstimatedPoints Points { get; } + + private AccelCalculator Calculator { get; } + + public void CalculateDots(double x, double y, double timeInMs) + { + var outVelocity = AccelCalculator.Velocity(x, y, timeInMs); + + (var inCombVel, var combSens, var combGain) = X.FindPointValuesFromOut(outVelocity); + Points.Velocity.Set(inCombVel, outVelocity); + Points.Sensitivity.Set(inCombVel, combSens); + Points.Gain.Set(inCombVel, combGain); + + } + + public void Clear() + { + X.Clear(); + } + + public void CreateGraphData(ManagedAccel accel, DriverSettings settings) + { + Clear(); + Calculator.Calculate(X, accel, settings.sensitivity.x, Calculator.SimulatedInputCombined); + } + } +} diff --git a/grapher/Models/Calculations/Data/AccelDataXYComponential.cs b/grapher/Models/Calculations/Data/AccelDataXYComponential.cs new file mode 100644 index 0000000..6231eb3 --- /dev/null +++ b/grapher/Models/Calculations/Data/AccelDataXYComponential.cs @@ -0,0 +1,64 @@ +using grapher.Models.Charts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace grapher.Models.Calculations.Data +{ + public class AccelDataXYComponential : IAccelData + { + public AccelDataXYComponential( + EstimatedPoints xPoints, + EstimatedPoints yPoints, + AccelCalculator calculator) + { + X = new AccelChartData(); + Y = new AccelChartData(); + XPoints = xPoints; + YPoints = yPoints; + Calculator = calculator; + } + + public AccelChartData X { get; } + + public AccelChartData Y { get; } + + private EstimatedPoints XPoints { get; } + + private EstimatedPoints YPoints { get; } + + private AccelCalculator Calculator { get; } + + public void CalculateDots(double x, double y, double timeInMs) + { + var outX = Math.Abs(x) / timeInMs; + var outY = Math.Abs(y) / timeInMs; + + (var inXVelocity, var xSensitivity, var xGain) = X.FindPointValuesFromOut(outX); + XPoints.Velocity.Set(inXVelocity, outX); + XPoints.Sensitivity.Set(inXVelocity, xSensitivity); + XPoints.Gain.Set(inXVelocity, xGain); + + (var inYVelocity, var ySensitivity, var yGain) = Y.FindPointValuesFromOut(outY); + YPoints.Velocity.Set(inYVelocity, outY); + YPoints.Sensitivity.Set(inYVelocity, ySensitivity); + YPoints.Gain.Set(inYVelocity, yGain); + + } + + public void Clear() + { + X.Clear(); + Y.Clear(); + } + + public void CreateGraphData(ManagedAccel accel, DriverSettings settings) + { + Clear(); + Calculator.Calculate(X, accel, settings.sensitivity.x, Calculator.SimulatedInputX); + Calculator.Calculate(Y, accel, settings.sensitivity.y, Calculator.SimulatedInputY); + } + } +} diff --git a/grapher/Models/Calculations/Data/AccelDataXYDirectional.cs b/grapher/Models/Calculations/Data/AccelDataXYDirectional.cs new file mode 100644 index 0000000..8bd889d --- /dev/null +++ b/grapher/Models/Calculations/Data/AccelDataXYDirectional.cs @@ -0,0 +1,84 @@ +using grapher.Models.Charts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace grapher.Models.Calculations.Data +{ + public class AccelDataXYDirectional : IAccelData + { + public AccelDataXYDirectional( + EstimatedPoints xPoints, + EstimatedPoints yPoints, + AccelCalculator calculator) + { + XPoints = xPoints; + YPoints = yPoints; + Calculator = calculator; + AngleToData = new AccelChartData[Constants.AngleDivisions]; + FillAngleData(); + } + + public AccelChartData X { get => AngleToData[0]; } + + public AccelChartData Y { get => AngleToData[Constants.AngleDivisions-1]; } + + public double SensitivityMax { get => X.MaxAccel; } + + public double SensitivityMin { get => X.MinAccel; } + + public double GainMax { get => X.MaxGain; } + + public double GainMin { get => X.MinGain; } + + private AccelChartData[] AngleToData { get; } + + private EstimatedPoints XPoints { get; } + + private EstimatedPoints YPoints { get; } + + private AccelCalculator Calculator { get; } + + public void CalculateDots(double x, double y, double timeInMs) + { + var outVelocity = AccelCalculator.Velocity(x, y, timeInMs); + var outAngle = Math.Atan2(Math.Abs(y),Math.Abs(x)); + var nearestAngleDivision = AccelCalculator.NearestAngleDivision(outAngle); + var data = AngleToData[nearestAngleDivision]; + var index = data.GetVelocityIndex(outVelocity); + var inVelocity = data.VelocityPoints.ElementAt(index).Key; + var xPoints = X.ValuesAtIndex(index); + var yPoints = Y.ValuesAtIndex(index); + XPoints.Sensitivity.Set(inVelocity, xPoints.Item1); + XPoints.Velocity.Set(inVelocity, xPoints.Item2); + XPoints.Gain.Set(inVelocity, xPoints.Item3); + YPoints.Sensitivity.Set(inVelocity, yPoints.Item1); + YPoints.Velocity.Set(inVelocity, yPoints.Item2); + YPoints.Gain.Set(inVelocity, yPoints.Item3); + } + + public void Clear() + { + foreach (var data in AngleToData) + { + data.Clear(); + } + } + + public void CreateGraphData(ManagedAccel accel, DriverSettings settings) + { + Clear(); + Calculator.CalculateDirectional(AngleToData, accel, settings, Calculator.SimulatedDirectionalInput); + } + + private void FillAngleData() + { + for(int i=0; i < Constants.AngleDivisions; i++) + { + AngleToData[i] = new AccelChartData(); + } + } + } +} diff --git a/grapher/Models/Calculations/Data/IAccelData.cs b/grapher/Models/Calculations/Data/IAccelData.cs new file mode 100644 index 0000000..576e6df --- /dev/null +++ b/grapher/Models/Calculations/Data/IAccelData.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace grapher.Models.Calculations.Data +{ + public interface IAccelData + { + void CalculateDots(double x, double y, double timeInMs); + + void CreateGraphData(ManagedAccel accel, DriverSettings settings); + + void Clear(); + + AccelChartData X { get; } + + AccelChartData Y { get; } + } +} diff --git a/grapher/Models/Charts/AccelCharts.cs b/grapher/Models/Charts/AccelCharts.cs index b7abb35..2369432 100644 --- a/grapher/Models/Charts/AccelCharts.cs +++ b/grapher/Models/Charts/AccelCharts.cs @@ -1,9 +1,8 @@ using grapher.Models.Calculations; +using grapher.Models.Calculations.Data; using grapher.Models.Charts; using grapher.Models.Charts.ChartState; -using grapher.Models.Serialized; using System; -using System.Drawing; using System.Windows.Forms; namespace grapher @@ -26,8 +25,14 @@ namespace grapher var estimatedX = new EstimatedPoints(); var estimatedY = new EstimatedPoints(); SetupCharts(sensitivityChart, velocityChart, gainChart, estimated, estimatedX, estimatedY); - var accelData = new AccelData(estimated, estimatedX, estimatedY); - ChartStateManager = new ChartStateManager(sensitivityChart, velocityChart, gainChart, accelData, accelCalculator); + ChartStateManager = new ChartStateManager( + sensitivityChart, + velocityChart, + gainChart, + accelCalculator, + estimated, + estimatedY, + estimatedX); ContainingForm = form; EnableVelocityAndGain = enableVelocityAndGain; @@ -56,7 +61,7 @@ namespace grapher private Button WriteButton { get; } - public AccelData AccelData + public IAccelData AccelData { get { @@ -80,14 +85,6 @@ namespace grapher } } - public int TopChartHeight - { - get - { - return ChartState.SensitivityChart.Height; - } - } - private int FormBorderHeight { get; } private ChartState ChartState { get; set; } diff --git a/grapher/Models/Charts/ChartState/ChartState.cs b/grapher/Models/Charts/ChartState/ChartState.cs index 0bb141e..5a86713 100644 --- a/grapher/Models/Charts/ChartState/ChartState.cs +++ b/grapher/Models/Charts/ChartState/ChartState.cs @@ -1,4 +1,5 @@ using grapher.Models.Calculations; +using grapher.Models.Calculations.Data; using grapher.Models.Serialized; using System; using System.Collections.Generic; @@ -15,13 +16,11 @@ namespace grapher.Models.Charts.ChartState ChartXY sensitivityChart, ChartXY velocityChart, ChartXY gainChart, - AccelData accelData, AccelCalculator calculator) { SensitivityChart = sensitivityChart; VelocityChart = velocityChart; GainChart = gainChart; - Data = accelData; Calculator = calculator; TwoDotsPerGraph = false; } @@ -32,7 +31,7 @@ namespace grapher.Models.Charts.ChartState public ChartXY GainChart { get; } - public AccelData Data { get; } + public IAccelData Data { get; protected set; } public AccelCalculator Calculator { get; } @@ -40,13 +39,19 @@ namespace grapher.Models.Charts.ChartState internal bool TwoDotsPerGraph { get; set; } - public abstract void MakeDots(double x, double y, double timeInMs); + public virtual void MakeDots(double x, double y, double timeInMs) + { + Data.CalculateDots(x, y, timeInMs); + } public abstract void Bind(); public abstract void Activate(); - public abstract void Calculate(ManagedAccel accel, DriverSettings settings); + public virtual void Calculate(ManagedAccel accel, DriverSettings settings) + { + Data.CreateGraphData(accel, settings); + } public void Redraw() { @@ -77,12 +82,14 @@ namespace grapher.Models.Charts.ChartState public void ShowVelocityAndGain() { + SensitivityChart.SetHeight(Constants.SensitivityChartTogetherHeight); VelocityChart.Show(); GainChart.Show(); } public void HideVelocityAndGain() { + SensitivityChart.SetHeight(Constants.SensitivityChartAloneHeight); VelocityChart.Hide(); GainChart.Hide(); } diff --git a/grapher/Models/Charts/ChartState/ChartStateManager.cs b/grapher/Models/Charts/ChartState/ChartStateManager.cs index 54d2e81..3d4bbec 100644 --- a/grapher/Models/Charts/ChartState/ChartStateManager.cs +++ b/grapher/Models/Charts/ChartState/ChartStateManager.cs @@ -14,28 +14,32 @@ namespace grapher.Models.Charts.ChartState ChartXY sensitivityChart, ChartXY velocityChart, ChartXY gainChat, - AccelData accelData, - AccelCalculator accelCalculator) + AccelCalculator accelCalculator, + EstimatedPoints combined, + EstimatedPoints xPoints, + EstimatedPoints yPoints) { CombinedState = new CombinedState( sensitivityChart, velocityChart, gainChat, - accelData, + combined, accelCalculator); XYOneGraphState = new XYOneGraphState( sensitivityChart, velocityChart, gainChat, - accelData, + xPoints, + yPoints, accelCalculator); XYTwoGraphState = new XYTwoGraphState( sensitivityChart, velocityChart, gainChat, - accelData, + xPoints, + yPoints, accelCalculator); } @@ -52,7 +56,9 @@ namespace grapher.Models.Charts.ChartState if (settings.combineMagnitudes) { - if (settings.sensitivity.x != settings.sensitivity.y) + if (settings.sensitivity.x != settings.sensitivity.y || + settings.domainArgs.domainXY.x != settings.domainArgs.domainXY.y || + settings.rangeXY.x != settings.rangeXY.y) { chartState = XYOneGraphState; } diff --git a/grapher/Models/Charts/ChartState/CombinedState.cs b/grapher/Models/Charts/ChartState/CombinedState.cs index 9eadb87..3511cec 100644 --- a/grapher/Models/Charts/ChartState/CombinedState.cs +++ b/grapher/Models/Charts/ChartState/CombinedState.cs @@ -1,4 +1,5 @@ using grapher.Models.Calculations; +using grapher.Models.Calculations.Data; using grapher.Models.Serialized; namespace grapher.Models.Charts.ChartState @@ -9,15 +10,16 @@ namespace grapher.Models.Charts.ChartState ChartXY sensitivityChart, ChartXY velocityChart, ChartXY gainChart, - AccelData accelData, + EstimatedPoints points, AccelCalculator accelCalculator) : base( sensitivityChart, velocityChart, gainChart, - accelData, accelCalculator) - { } + { + Data = new AccelDataCombined(points, accelCalculator); + } public override void Activate() { @@ -30,23 +32,13 @@ namespace grapher.Models.Charts.ChartState GainChart.ClearSecondDots(); } - public override void MakeDots(double x, double y, double timeInMs) - { - Data.CalculateDots(x, y, timeInMs); - } - public override void Bind() { - SensitivityChart.Bind(Data.Combined.AccelPoints); - VelocityChart.Bind(Data.Combined.VelocityPoints); - GainChart.Bind(Data.Combined.GainPoints); - SensitivityChart.SetMinMax(Data.Combined.MinAccel, Data.Combined.MaxAccel); - GainChart.SetMinMax(Data.Combined.MinGain, Data.Combined.MaxGain); - } - - public override void Calculate(ManagedAccel accel, DriverSettings settings) - { - Calculator.Calculate(Data.Combined, accel, settings.sensitivity.x, Calculator.SimulatedInputCombined); + SensitivityChart.Bind(Data.X.AccelPoints); + VelocityChart.Bind(Data.X.VelocityPoints); + GainChart.Bind(Data.X.GainPoints); + SensitivityChart.SetMinMax(Data.X.MinAccel, Data.X.MaxAccel); + GainChart.SetMinMax(Data.X.MinGain, Data.X.MaxGain); } } } diff --git a/grapher/Models/Charts/ChartState/XYOneGraphState.cs b/grapher/Models/Charts/ChartState/XYOneGraphState.cs index 2b3cd9c..34e9070 100644 --- a/grapher/Models/Charts/ChartState/XYOneGraphState.cs +++ b/grapher/Models/Charts/ChartState/XYOneGraphState.cs @@ -1,4 +1,5 @@ using grapher.Models.Calculations; +using grapher.Models.Calculations.Data; using grapher.Models.Serialized; namespace grapher.Models.Charts.ChartState @@ -9,18 +10,22 @@ namespace grapher.Models.Charts.ChartState ChartXY sensitivityChart, ChartXY velocityChart, ChartXY gainChart, - AccelData accelData, + EstimatedPoints xPoints, + EstimatedPoints yPoints, AccelCalculator accelCalculator) : base( sensitivityChart, velocityChart, gainChart, - accelData, accelCalculator) { + DataDirectional = new AccelDataXYDirectional(xPoints, yPoints, accelCalculator); + Data = DataDirectional; TwoDotsPerGraph = true; } + private AccelDataXYDirectional DataDirectional { get; } + public override void Activate() { SensitivityChart.SetCombined(); @@ -28,23 +33,13 @@ namespace grapher.Models.Charts.ChartState GainChart.SetCombined(); } - public override void MakeDots(double x, double y, double timeInMs) - { - Data.CalculateDotsCombinedDiffSens(x, y, timeInMs, Settings); - } - public override void Bind() { SensitivityChart.BindXYCombined(Data.X.AccelPoints, Data.Y.AccelPoints); VelocityChart.BindXYCombined(Data.X.VelocityPoints, Data.Y.VelocityPoints); GainChart.BindXYCombined(Data.X.GainPoints, Data.Y.GainPoints); - SensitivityChart.SetMinMax(Data.Combined.MinAccel, Data.Combined.MaxAccel); - GainChart.SetMinMax(Data.Combined.MinGain, Data.Combined.MaxGain); - } - - public override void Calculate(ManagedAccel accel, DriverSettings settings) - { - Calculator.CalculateCombinedDiffSens(Data, accel, settings, Calculator.SimulatedInputCombined); + SensitivityChart.SetMinMax(DataDirectional.SensitivityMin, DataDirectional.SensitivityMax); + GainChart.SetMinMax(DataDirectional.GainMin, DataDirectional.GainMax); } } } diff --git a/grapher/Models/Charts/ChartState/XYTwoGraphState.cs b/grapher/Models/Charts/ChartState/XYTwoGraphState.cs index 732ea3c..5b6c2b8 100644 --- a/grapher/Models/Charts/ChartState/XYTwoGraphState.cs +++ b/grapher/Models/Charts/ChartState/XYTwoGraphState.cs @@ -1,4 +1,5 @@ using grapher.Models.Calculations; +using grapher.Models.Calculations.Data; using grapher.Models.Serialized; using System; @@ -10,15 +11,17 @@ namespace grapher.Models.Charts.ChartState ChartXY sensitivityChart, ChartXY velocityChart, ChartXY gainChart, - AccelData accelData, + EstimatedPoints xPoints, + EstimatedPoints yPoints, AccelCalculator accelCalculator) : base( sensitivityChart, velocityChart, gainChart, - accelData, accelCalculator) - { } + { + Data = new AccelDataXYComponential(xPoints, yPoints, accelCalculator); + } public override DriverSettings Settings { get; set; } @@ -33,11 +36,6 @@ namespace grapher.Models.Charts.ChartState GainChart.ClearSecondDots(); } - public override void MakeDots(double x, double y, double timeInMs) - { - Data.CalculateDotsXY(x, y, timeInMs); - } - public override void Bind() { SensitivityChart.BindXY(Data.X.AccelPoints, Data.Y.AccelPoints); @@ -47,11 +45,5 @@ namespace grapher.Models.Charts.ChartState SensitivityChart.SetMinMaxXY(Data.X.MinAccel, Data.X.MaxAccel, Data.Y.MinAccel, Data.Y.MaxAccel); GainChart.SetMinMaxXY(Data.X.MinGain, Data.X.MaxGain, Data.Y.MinGain, Data.Y.MaxGain); } - - public override void Calculate(ManagedAccel accel, DriverSettings settings) - { - Calculator.Calculate(Data.X, accel, settings.sensitivity.x, Calculator.SimulatedInputX); - Calculator.Calculate(Data.Y, accel, settings.sensitivity.y, Calculator.SimulatedInputY); - } } } diff --git a/grapher/Models/Charts/ChartXY.cs b/grapher/Models/Charts/ChartXY.cs index 553ab3e..bd80ea2 100644 --- a/grapher/Models/Charts/ChartXY.cs +++ b/grapher/Models/Charts/ChartXY.cs @@ -244,8 +244,8 @@ namespace grapher ChartX.ChartAreas[0].AxisY.Maximum = maxX * (1 + VerticalMargin); VerifyRange(minY, maxY); - ChartX.ChartAreas[0].AxisY.Minimum = minY * (1 - VerticalMargin); - ChartX.ChartAreas[0].AxisY.Maximum = maxY * (1 + VerticalMargin); + ChartY.ChartAreas[0].AxisY.Minimum = minY * (1 - VerticalMargin); + ChartY.ChartAreas[0].AxisY.Maximum = maxY * (1 + VerticalMargin); } public void SetCombined() diff --git a/grapher/Models/Fields/FieldXY.cs b/grapher/Models/Fields/FieldXY.cs index a7db922..72f14ea 100644 --- a/grapher/Models/Fields/FieldXY.cs +++ b/grapher/Models/Fields/FieldXY.cs @@ -7,13 +7,14 @@ namespace grapher { #region Constructors - public FieldXY(TextBox xBox, TextBox yBox, CheckBox lockCheckBox, Form containingForm, double defaultData) + public FieldXY(TextBox xBox, TextBox yBox, CheckBox lockCheckBox, Form containingForm, double defaultData, bool allowCombined = true) { XField = new Field(xBox, containingForm, defaultData); YField = new Field(yBox, containingForm, defaultData); YField.FormatString = Constants.ShortenedFormatString; LockCheckBox = lockCheckBox; LockCheckBox.CheckedChanged += new System.EventHandler(CheckChanged); + AllowCombined = allowCombined; XField.Box.Width = (YField.Box.Left + YField.Box.Width - XField.Box.Left - Constants.DefaultFieldSeparation) / 2; YField.Box.Width = XField.Box.Width; @@ -24,7 +25,7 @@ namespace grapher YField.Box.Left = XField.Box.Left + XField.Box.Width + Constants.DefaultFieldSeparation; CombinedWidth = DefaultWidthX + DefaultWidthY + YField.Box.Left - (XField.Box.Left + DefaultWidthX); - SetCombined(); + Startup(); } #endregion Constructors @@ -116,6 +117,7 @@ namespace grapher private int DefaultWidthY { get; } + private bool AllowCombined { get; } #endregion Properties @@ -127,9 +129,10 @@ namespace grapher YField.SetNewDefault(y); XField.SetToDefault(); - if (x != y) + if (x != y || !AllowCombined) { LockCheckBox.Checked = false; + YField.SetToDefault(); if (Combined) { @@ -147,6 +150,20 @@ namespace grapher } } + private void Startup() + { + if (AllowCombined) + { + SetCombined(); + } + else + { + SetSeparate(); + LockCheckBox.Hide(); + LockCheckBox.Enabled = false; + } + } + private void CheckChanged(object sender, EventArgs e) { if (LockCheckBox.CheckState == CheckState.Checked) @@ -161,11 +178,14 @@ namespace grapher public void SetCombined() { - Combined = true; - YField.SetToUnavailable(); - YField.Hide(); - XField.Box.Width = CombinedWidth; - XField.FormatString = Constants.DefaultFieldFormatString; + if (AllowCombined) + { + Combined = true; + YField.SetToUnavailable(); + YField.Hide(); + XField.Box.Width = CombinedWidth; + XField.FormatString = Constants.DefaultFieldFormatString; + } } public void SetSeparate() diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs index 151a2a2..163829f 100644 --- a/grapher/Models/Mouse/MouseWatcher.cs +++ b/grapher/Models/Mouse/MouseWatcher.cs @@ -1,6 +1,7 @@ using grapher.Models.Serialized; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; @@ -701,6 +702,8 @@ namespace grapher.Models.Mouse RAWINPUTDEVICE[] devices = new RAWINPUTDEVICE[1]; devices[0] = device; RegisterRawInputDevices(devices, 1, Marshal.SizeOf(typeof(RAWINPUTDEVICE))); + Stopwatch = new Stopwatch(); + Stopwatch.Start(); } #endregion Constructors @@ -717,6 +720,8 @@ namespace grapher.Models.Mouse private MouseData MouseData { get; } + private Stopwatch Stopwatch { get; } + private List<IntPtr> DeviceHandles { get; } private bool AnyDevice { get; set; } @@ -757,6 +762,11 @@ namespace grapher.Models.Mouse if (relative && deviceMatch && (rawInput.Data.Mouse.LastX != 0 || rawInput.Data.Mouse.LastY != 0)) { + var time = Stopwatch.Elapsed.TotalMilliseconds; + Stopwatch.Restart(); + time = time > 100 ? 100 : time; + time = time > (PollTime * 0.8) ? time : (PollTime * 0.8); + double x = rawInput.Data.Mouse.LastX; double y = rawInput.Data.Mouse.LastY; @@ -776,7 +786,7 @@ namespace grapher.Models.Mouse } MouseData.Set(rawInput.Data.Mouse.LastX, rawInput.Data.Mouse.LastY); - AccelCharts.MakeDots(x, y, PollTime); + AccelCharts.MakeDots(x, y, time); } } diff --git a/grapher/Models/Options/AccelTypeOptions.cs b/grapher/Models/Options/AccelTypeOptions.cs index 4410a12..8d3fecb 100644 --- a/grapher/Models/Options/AccelTypeOptions.cs +++ b/grapher/Models/Options/AccelTypeOptions.cs @@ -289,7 +289,6 @@ namespace grapher Limit, Exponent, Midpoint, - WriteButton, top); } diff --git a/grapher/Models/Options/ActiveValueLabelXY.cs b/grapher/Models/Options/ActiveValueLabelXY.cs index 381779c..f5b593e 100644 --- a/grapher/Models/Options/ActiveValueLabelXY.cs +++ b/grapher/Models/Options/ActiveValueLabelXY.cs @@ -140,6 +140,18 @@ namespace grapher.Models.Options } } + public void Hide() + { + X.Hide(); + Y.Hide(); + } + + public void Show() + { + X.Show(); + Y.Show(); + } + private void Align (int width) { FullWidth = width; diff --git a/grapher/Models/Options/ApplyOptions.cs b/grapher/Models/Options/ApplyOptions.cs index b8cc9bf..ffe430d 100644 --- a/grapher/Models/Options/ApplyOptions.cs +++ b/grapher/Models/Options/ApplyOptions.cs @@ -1,4 +1,5 @@ -using grapher.Models.Serialized; +using grapher.Models.Options.Directionality; +using grapher.Models.Serialized; using System; using System.Drawing; using System.Windows.Forms; @@ -10,24 +11,24 @@ namespace grapher.Models.Options #region Constructors public ApplyOptions( - ToolStripMenuItem wholeVectorMenuItem, - ToolStripMenuItem byComponentMenuItem, CheckBox byComponentVectorXYLock, AccelOptionSet optionSetX, AccelOptionSet optionSetY, + DirectionalityOptions directionalityOptions, OptionXY sensitivity, Option rotation, Label lockXYLabel, AccelCharts accelCharts) { - WholeVectorMenuItem = wholeVectorMenuItem; - ByComponentVectorMenuItem = byComponentMenuItem; + Directionality = directionalityOptions; + WholeVectorCheckBox = Directionality.WholeCheckBox; + ByComponentVectorCheckBox = Directionality.ByComponentCheckBox; - WholeVectorMenuItem.Click += new System.EventHandler(OnWholeClicked); - ByComponentVectorMenuItem.Click += new System.EventHandler(OnByComponentClicked); + WholeVectorCheckBox.Click += new System.EventHandler(OnWholeClicked); + ByComponentVectorCheckBox.Click += new System.EventHandler(OnByComponentClicked); - WholeVectorMenuItem.CheckedChanged += new System.EventHandler(OnWholeCheckedChange); - ByComponentVectorMenuItem.CheckedChanged += new System.EventHandler(OnByComponentCheckedChange); + WholeVectorCheckBox.CheckedChanged += new System.EventHandler(OnWholeCheckedChange); + ByComponentVectorCheckBox.CheckedChanged += new System.EventHandler(OnByComponentCheckedChange); ByComponentVectorXYLock = byComponentVectorXYLock; OptionSetX = optionSetX; @@ -52,9 +53,9 @@ namespace grapher.Models.Options #region Properties - public ToolStripMenuItem WholeVectorMenuItem { get; } + public CheckBox WholeVectorCheckBox { get; } - public ToolStripMenuItem ByComponentVectorMenuItem { get; } + public CheckBox ByComponentVectorCheckBox { get; } public CheckBox ByComponentVectorXYLock { get; } @@ -62,6 +63,8 @@ namespace grapher.Models.Options public AccelOptionSet OptionSetY { get; } + public DirectionalityOptions Directionality { get; } + public OptionXY Sensitivity { get; } public Option Rotation { get; } @@ -114,8 +117,8 @@ namespace grapher.Models.Options Sensitivity.SetActiveValues(xSens, ySens); Rotation.SetActiveValue(rotation); OptionSetX.SetActiveValues(xMode, xArgs); - WholeVectorMenuItem.Checked = isWhole; - ByComponentVectorMenuItem.Checked = !isWhole; + WholeVectorCheckBox.Checked = isWhole; + ByComponentVectorCheckBox.Checked = !isWhole; ByComponentVectorXYLock.Checked = xArgs.Equals(yArgs); OptionSetY.SetActiveValues(yMode, yArgs); } @@ -132,6 +135,8 @@ namespace grapher.Models.Options settings.args.y, settings.combineMagnitudes); + Directionality.SetActiveValues(settings); + AccelCharts.SetLogarithmic( OptionSetX.Options.AccelerationType.LogarithmicCharts, OptionSetY.Options.AccelerationType.LogarithmicCharts); @@ -139,25 +144,27 @@ namespace grapher.Models.Options public void OnWholeClicked(object sender, EventArgs e) { - if (!WholeVectorMenuItem.Checked) + if (!WholeVectorCheckBox.Checked) { - WholeVectorMenuItem.Checked = true; - ByComponentVectorMenuItem.Checked = false; + WholeVectorCheckBox.Checked = true; + ByComponentVectorCheckBox.Checked = false; + Directionality.ToWhole(); } } public void OnByComponentClicked(object sender, EventArgs e) { - if (!ByComponentVectorMenuItem.Checked) + if (!ByComponentVectorCheckBox.Checked) { - WholeVectorMenuItem.Checked = false; - ByComponentVectorMenuItem.Checked = true; + WholeVectorCheckBox.Checked = false; + ByComponentVectorCheckBox.Checked = true; + Directionality.ToByComponent(); } } public void OnWholeCheckedChange(object sender, EventArgs e) { - if (WholeVectorMenuItem.Checked) + if (WholeVectorCheckBox.Checked) { EnableWholeApplication(); } @@ -165,7 +172,7 @@ namespace grapher.Models.Options public void OnByComponentCheckedChange(object sender, EventArgs e) { - if (ByComponentVectorMenuItem.Checked) + if (ByComponentVectorCheckBox.Checked) { EnableByComponentApplication(); } diff --git a/grapher/Models/Options/Directionality/DirectionalityOptions.cs b/grapher/Models/Options/Directionality/DirectionalityOptions.cs new file mode 100644 index 0000000..c21b932 --- /dev/null +++ b/grapher/Models/Options/Directionality/DirectionalityOptions.cs @@ -0,0 +1,213 @@ +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.Models.Options.Directionality +{ + public class DirectionalityOptions + { + public DirectionalityOptions( + Panel containingPanel, + Label directionalityLabel, + Label directionalityX, + Label directionalityY, + Label directionalityActiveValueTitle, + Option lpNorm, + OptionXY domain, + OptionXY range, + CheckBox wholeCheckBox, + CheckBox byComponentCheckBox, + int top) + { + ContainingPanel = containingPanel; + DirectionalityLabel = directionalityLabel; + DirectionalityX = directionalityX; + DirectionalityY = directionalityY; + DirectionalityActiveValueTitle = directionalityActiveValueTitle; + LpNorm = lpNorm; + Domain = domain; + Range = range; + WholeCheckBox = wholeCheckBox; + ByComponentCheckBox = byComponentCheckBox; + + ContainingPanel.Paint += Panel_Paint; + DirectionalityLabel.Click += Title_click; + ContainingPanel.Top = top; + DirectionalityLabel.Left = Constants.DirectionalityTitlePad; + DirectionalityLabel.Top = Constants.DirectionalityTitlePad; + IsHidden = false; + ToWhole(); + Hide(); + } + + public Panel ContainingPanel { get; } + + public Label DirectionalityLabel { get; } + + public Label DirectionalityX { get; } + + public Label DirectionalityY { get; } + + public Label DirectionalityActiveValueTitle { get; } + + public Option LpNorm { get; } + + public OptionXY Domain { get; } + + public OptionXY Range { get; } + + public CheckBox WholeCheckBox { get; } + + public CheckBox ByComponentCheckBox { get; } + + public int OpenHeight { get => WholeCheckBox.Bottom - DirectionalityLabel.Top + 2 * Constants.DirectionalityTitlePad; } + + public int ClosedHeight { get => DirectionalityLabel.Height + 2 * Constants.DirectionalityTitlePad; } + + private bool IsHidden { get; set; } + + public DomainArgs GetDomainArgs() + { + if (!ByComponentCheckBox.Checked) + { + return new DomainArgs + { + domainXY = new Vec2<double> + { + x = Domain.Fields.X, + y = Domain.Fields.Y, + }, + lpNorm = LpNorm.Field.Data, + }; + } + else + { + return new DomainArgs + { + domainXY = new Vec2<double> + { + x = 1, + y = 1, + }, + lpNorm = 2, + }; + + } + } + + public Vec2<double> GetRangeXY() + { + if (!ByComponentCheckBox.Checked) + { + return new Vec2<double> + { + x = Range.Fields.X, + y = Range.Fields.Y, + }; + } + else + { + return new Vec2<double> + { + x = 1, + y = 1, + }; + } + + } + + public void SetActiveValues(DriverSettings settings) + { + Domain.SetActiveValues(settings.domainArgs.domainXY.x, settings.domainArgs.domainXY.y); + LpNorm.SetActiveValue(settings.domainArgs.lpNorm); + Range.SetActiveValues(settings.rangeXY.x, settings.rangeXY.y); + } + + public void Hide() + { + if (!IsHidden) + { + DirectionalityX.Hide(); + DirectionalityY.Hide(); + DirectionalityActiveValueTitle.Hide(); + LpNorm.Hide(); + Domain.Hide(); + Range.Hide(); + WholeCheckBox.Hide(); + ByComponentCheckBox.Hide(); + DirectionalityLabel.Text = Constants.DirectionalityTitleClosed; + DrawHidden(); + IsHidden = true; + } + } + + public void Show() + { + if (IsHidden) + { + DirectionalityX.Show(); + DirectionalityY.Show(); + DirectionalityActiveValueTitle.Show(); + LpNorm.Show(); + Domain.Show(); + Range.Show(); + Range.Fields.LockCheckBox.Hide(); + WholeCheckBox.Show(); + ByComponentCheckBox.Show(); + DirectionalityLabel.Text = Constants.DirectionalityTitleOpen; + DrawShown(); + IsHidden = false; + } + } + + public void ToByComponent() + { + LpNorm.SetToUnavailable(); + Domain.SetToUnavailable(); + Range.SetToUnavailable(); + } + + public void ToWhole() + { + LpNorm.SetToAvailable(); + Domain.SetToAvailable(); + Range.SetToAvailable(); + } + + private void DrawHidden() + { + ContainingPanel.Height = ClosedHeight; + ContainingPanel.Invalidate(); + } + + private void DrawShown() + { + ContainingPanel.Height = OpenHeight; + ContainingPanel.Invalidate(); + } + + private void Panel_Paint(object sender, PaintEventArgs e) + { + Color col = Color.DarkGray; + ButtonBorderStyle bbs = ButtonBorderStyle.Dashed; + int thickness = 2; + ControlPaint.DrawBorder(e.Graphics, this.ContainingPanel.ClientRectangle, col, thickness, bbs, col, thickness, bbs, col, thickness, bbs, col, thickness, bbs); + } + + private void Title_click(object sender, EventArgs e) + { + if (IsHidden) + { + Show(); + } + else + { + Hide(); + } + } + } +} diff --git a/grapher/Models/Options/Option.cs b/grapher/Models/Options/Option.cs index d5129d5..c6b68ba 100644 --- a/grapher/Models/Options/Option.cs +++ b/grapher/Models/Options/Option.cs @@ -171,6 +171,16 @@ namespace grapher ActiveValueLabel.Align(); } + public void SetToUnavailable() + { + Field.SetToUnavailable(); + } + + public void SetToAvailable() + { + Field.SetToDefault(); + } + #endregion Methods } } diff --git a/grapher/Models/Options/OptionXY.cs b/grapher/Models/Options/OptionXY.cs index b4a2b27..102d05d 100644 --- a/grapher/Models/Options/OptionXY.cs +++ b/grapher/Models/Options/OptionXY.cs @@ -22,8 +22,9 @@ namespace grapher Form containingForm, double defaultData, Label label, - ActiveValueLabelXY activeValueLabels) - : this(new FieldXY(xBox, yBox, lockCheckBox, containingForm, defaultData), label, activeValueLabels) + ActiveValueLabelXY activeValueLabels, + bool allowCombined = true) + : this(new FieldXY(xBox, yBox, lockCheckBox, containingForm, defaultData, allowCombined), label, activeValueLabels) { } @@ -35,7 +36,8 @@ namespace grapher double defaultData, Label label, ActiveValueLabelXY activeValueLabels, - string startingName): + string startingName, + bool allowCombined = true): this( xBox, yBox, @@ -43,7 +45,8 @@ namespace grapher containingForm, defaultData, label, - activeValueLabels) + activeValueLabels, + allowCombined) { SetName(startingName); } @@ -124,7 +127,6 @@ namespace grapher { ActiveValueLabels.SetValues(x, y); Fields.SetActive(x, y); - } public override void AlignActiveValues() @@ -137,6 +139,7 @@ namespace grapher Fields.Hide(); Fields.LockCheckBox.Hide(); Label.Hide(); + ActiveValueLabels.Hide(); } public void Show() @@ -144,6 +147,7 @@ namespace grapher Fields.Show(); Fields.LockCheckBox.Show(); Label.Show(); + ActiveValueLabels.Show(); } public override void Show(string name) @@ -153,6 +157,18 @@ namespace grapher Show(); } + public void SetToUnavailable() + { + Fields.XField.SetToUnavailable(); + Fields.YField.SetToUnavailable(); + } + + public void SetToAvailable() + { + Fields.XField.SetToDefault(); + Fields.YField.SetToDefault(); + } + #endregion Methods } } diff --git a/grapher/grapher.csproj b/grapher/grapher.csproj index 35d7e0c..8673325 100644 --- a/grapher/grapher.csproj +++ b/grapher/grapher.csproj @@ -90,7 +90,10 @@ <Compile Include="Models\AccelGUIFactory.cs" /> <Compile Include="Models\Calculations\AccelCalculator.cs" /> <Compile Include="Models\Calculations\AccelChartData.cs" /> - <Compile Include="Models\Calculations\AccelData.cs" /> + <Compile Include="Models\Calculations\Data\AccelDataCombined.cs" /> + <Compile Include="Models\Calculations\Data\AccelDataXYComponential.cs" /> + <Compile Include="Models\Calculations\Data\AccelDataXYDirectional.cs" /> + <Compile Include="Models\Calculations\Data\IAccelData.cs" /> <Compile Include="Models\Charts\AccelCharts.cs" /> <Compile Include="Models\AccelGUI.cs" /> <Compile Include="Models\Charts\ChartState\ChartState.cs" /> @@ -126,6 +129,7 @@ <Compile Include="Layouts\NaturalLayout.cs" /> <Compile Include="Layouts\OffLayout.cs" /> <Compile Include="Layouts\PowerLayout.cs" /> + <Compile Include="Models\Options\Directionality\DirectionalityOptions.cs" /> <Compile Include="Models\Options\IOption.cs" /> <Compile Include="Models\Options\OffsetOptions.cs" /> <Compile Include="Models\Options\Option.cs" /> diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index 2c0d5e3..46e7e3a 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -54,6 +54,14 @@ public value struct Vec2 }; [JsonObject(ItemRequired = Required::Always)] +[StructLayout(LayoutKind::Sequential)] +public value struct DomainArgs +{ + Vec2<double> domainXY; + double lpNorm; +}; + +[JsonObject(ItemRequired = Required::Always)] [StructLayout(LayoutKind::Sequential, CharSet = CharSet::Unicode)] public ref struct DriverSettings { @@ -78,6 +86,12 @@ public ref struct DriverSettings [JsonProperty("Negative directional multipliers", Required = Required::Default)] Vec2<double> directionalMultipliers; + [JsonProperty("Stretches domain for horizontal vs vertical inputs")] + DomainArgs domainArgs; + + [JsonProperty("Stretches accel range for horizontal vs vertical inputs")] + Vec2<double> rangeXY; + [JsonProperty(Required = Required::Default)] double minimumTime; @@ -183,16 +197,40 @@ error_list_t^ get_accel_errors(AccelMode mode, AccelArgs^ args) return error_list; } +error_list_t^ get_other_errors(DriverSettings^ settings) +{ + auto error_list = gcnew error_list_t(); + + if (settings->rangeXY.x <= 0 || settings->rangeXY.y <= 0) + { + error_list->Add("range values must be positive"); + } + + if (settings->domainArgs.domainXY.x <= 0 || settings->domainArgs.domainXY.y <= 0) + { + error_list->Add("domain values must be positive"); + } + + if (settings->domainArgs.lpNorm <= 0) + { + error_list->Add("lp norm must be positive"); + } + + return error_list; +} + public ref class SettingsErrors { public: error_list_t^ x; error_list_t^ y; + error_list_t^ other; bool Empty() { return (x == nullptr || x->Count == 0) && - (y == nullptr || y->Count == 0); + (y == nullptr || y->Count == 0) && + (other == nullptr || other->Count == 0); } virtual String^ ToString() override @@ -219,6 +257,11 @@ public: sb->AppendFormat("y: {0}\n", str); } } + + for each (String ^ str in other) + { + sb->AppendLine(str); + } return sb->ToString(); } @@ -272,9 +315,13 @@ public ref struct DriverInterop errors->y = get_accel_errors(args->modes.y, args->args.y); } + errors->other = get_other_errors(args); + return errors; } + + static error_list_t^ GetAccelErrors(AccelMode mode, AccelArgs^ args) { return get_accel_errors(mode, args); |