From f145f3cc68c0f192a7881dc2caeec39267ec1d7e Mon Sep 17 00:00:00 2001
From: Hathor86 <francois.guiot@gmail.com>
Date: Sat, 23 Jan 2016 16:16:19 +0100
Subject: [PATCH] Polar Coord

- Add polar coordinates to VirtualPadAnalogStick
- Update N64 pad Schema as VirtualPadAnalogStick is a bit larger
- Add Clamp method to NumberExtensions (allows a IComparable<T> value to be restriced between 2 specified other values)
---
 .../tools/VirtualPads/VirtualPad.cs           |   2 +-
 .../VirtualPadAnalogStick.Designer.cs         | 104 ++++++++++++++----
 .../controls/VirtualPadAnalogStick.cs         |  81 ++++++++------
 .../controls/components/AnalogSticklPanel.cs  |   6 +-
 .../tools/VirtualPads/schema/N64Schema.cs     |  30 ++---
 BizHawk.Common/Extensions/NumberExtensions.cs |  27 ++++-
 6 files changed, 178 insertions(+), 72 deletions(-)

diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/VirtualPad.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/VirtualPad.cs
index aa8238de3d..1c1935bb36 100644
--- a/BizHawk.Client.EmuHawk/tools/VirtualPads/VirtualPad.cs
+++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/VirtualPad.cs
@@ -85,7 +85,7 @@ namespace BizHawk.Client.EmuHawk
 						{
 							Name = button.Name,
 							Location = UIHelper.Scale(button.Location),
-							Size = UIHelper.Scale(new Size(127 + 79, 127 + 9)),
+							Size = UIHelper.Scale(new Size(180 + 79, 200 + 9)),
 							RangeX = new float[] { button.MinValue, button.MidValue, button.MaxValue },
 							RangeY = new float[] { button.MinValueSec, button.MidValueSec, button.MaxValueSec }, 
 						});
diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.Designer.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.Designer.cs
index 6245962f67..2d3abd745e 100644
--- a/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.Designer.cs
+++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.Designer.cs
@@ -35,18 +35,24 @@
 			this.MaxLabel = new System.Windows.Forms.Label();
 			this.MaxXNumeric = new System.Windows.Forms.NumericUpDown();
 			this.MaxYNumeric = new System.Windows.Forms.NumericUpDown();
+			this.rLabel = new System.Windows.Forms.Label();
+			this.manualR = new System.Windows.Forms.NumericUpDown();
+			this.manualTheta = new System.Windows.Forms.NumericUpDown();
+			this.thetaLabel = new System.Windows.Forms.Label();
 			this.AnalogStick = new BizHawk.Client.EmuHawk.AnalogStickPanel();
 			((System.ComponentModel.ISupportInitialize)(this.ManualX)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.ManualY)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.MaxXNumeric)).BeginInit();
 			((System.ComponentModel.ISupportInitialize)(this.MaxYNumeric)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.manualR)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.manualTheta)).BeginInit();
 			this.SuspendLayout();
 			// 
 			// XLabel
 			// 
 			this.XLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.XLabel.AutoSize = true;
-			this.XLabel.Location = new System.Drawing.Point(138, 7);
+			this.XLabel.Location = new System.Drawing.Point(187, 7);
 			this.XLabel.Name = "XLabel";
 			this.XLabel.Size = new System.Drawing.Size(14, 13);
 			this.XLabel.TabIndex = 23;
@@ -55,7 +61,7 @@
 			// ManualX
 			// 
 			this.ManualX.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.ManualX.Location = new System.Drawing.Point(156, 3);
+			this.ManualX.Location = new System.Drawing.Point(205, 3);
 			this.ManualX.Maximum = new decimal(new int[] {
             127,
             0,
@@ -69,14 +75,13 @@
 			this.ManualX.Name = "ManualX";
 			this.ManualX.Size = new System.Drawing.Size(44, 20);
 			this.ManualX.TabIndex = 24;
-			this.ManualX.ValueChanged += new System.EventHandler(this.ManualX_ValueChanged);
-			this.ManualX.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ManualX_KeyUp);
+			this.ManualX.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ManualXY_ValueChanged);
 			// 
 			// YLabel
 			// 
 			this.YLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.YLabel.AutoSize = true;
-			this.YLabel.Location = new System.Drawing.Point(138, 33);
+			this.YLabel.Location = new System.Drawing.Point(187, 33);
 			this.YLabel.Name = "YLabel";
 			this.YLabel.Size = new System.Drawing.Size(14, 13);
 			this.YLabel.TabIndex = 26;
@@ -85,7 +90,7 @@
 			// ManualY
 			// 
 			this.ManualY.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.ManualY.Location = new System.Drawing.Point(156, 29);
+			this.ManualY.Location = new System.Drawing.Point(205, 29);
 			this.ManualY.Maximum = new decimal(new int[] {
             127,
             0,
@@ -100,14 +105,13 @@
 			this.ManualY.RightToLeft = System.Windows.Forms.RightToLeft.No;
 			this.ManualY.Size = new System.Drawing.Size(44, 20);
 			this.ManualY.TabIndex = 25;
-			this.ManualY.ValueChanged += new System.EventHandler(this.ManualY_ValueChanged);
-			this.ManualY.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ManualY_KeyUp);
+			this.ManualY.KeyUp += new System.Windows.Forms.KeyEventHandler(this.ManualXY_ValueChanged);
 			// 
 			// MaxLabel
 			// 
 			this.MaxLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
 			this.MaxLabel.AutoSize = true;
-			this.MaxLabel.Location = new System.Drawing.Point(138, 72);
+			this.MaxLabel.Location = new System.Drawing.Point(205, 107);
 			this.MaxLabel.Name = "MaxLabel";
 			this.MaxLabel.Size = new System.Drawing.Size(47, 13);
 			this.MaxLabel.TabIndex = 27;
@@ -116,7 +120,7 @@
 			// MaxXNumeric
 			// 
 			this.MaxXNumeric.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.MaxXNumeric.Location = new System.Drawing.Point(138, 89);
+			this.MaxXNumeric.Location = new System.Drawing.Point(205, 124);
 			this.MaxXNumeric.Maximum = new decimal(new int[] {
             127,
             0,
@@ -130,13 +134,13 @@
 			this.MaxXNumeric.Name = "MaxXNumeric";
 			this.MaxXNumeric.Size = new System.Drawing.Size(44, 20);
 			this.MaxXNumeric.TabIndex = 28;
-			this.MaxXNumeric.ValueChanged += new System.EventHandler(this.MaxXNumeric_ValueChanged);
-			this.MaxXNumeric.KeyUp += new System.Windows.Forms.KeyEventHandler(this.MaxXNumeric_KeyUp);
+			this.MaxXNumeric.ValueChanged += new System.EventHandler(this.MaxManualXY_ValueChanged);
+			this.MaxXNumeric.KeyUp += new System.Windows.Forms.KeyEventHandler(this.MaxManualXY_ValueChanged);
 			// 
 			// MaxYNumeric
 			// 
 			this.MaxYNumeric.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
-			this.MaxYNumeric.Location = new System.Drawing.Point(138, 112);
+			this.MaxYNumeric.Location = new System.Drawing.Point(205, 147);
 			this.MaxYNumeric.Maximum = new decimal(new int[] {
             127,
             0,
@@ -151,21 +155,71 @@
 			this.MaxYNumeric.RightToLeft = System.Windows.Forms.RightToLeft.No;
 			this.MaxYNumeric.Size = new System.Drawing.Size(44, 20);
 			this.MaxYNumeric.TabIndex = 29;
-			this.MaxYNumeric.ValueChanged += new System.EventHandler(this.MaxYNumeric_ValueChanged);
-			this.MaxYNumeric.KeyUp += new System.Windows.Forms.KeyEventHandler(this.MaxYNumeric_KeyUp);
+			this.MaxYNumeric.ValueChanged += new System.EventHandler(this.MaxManualXY_ValueChanged);
+			this.MaxYNumeric.KeyUp += new System.Windows.Forms.KeyEventHandler(this.MaxManualXY_ValueChanged);
+			// 
+			// rLabel
+			// 
+			this.rLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.rLabel.AutoSize = true;
+			this.rLabel.Location = new System.Drawing.Point(167, 60);
+			this.rLabel.Name = "rLabel";
+			this.rLabel.Size = new System.Drawing.Size(26, 13);
+			this.rLabel.TabIndex = 30;
+			this.rLabel.Text = "Ray";
+			// 
+			// manualR
+			// 
+			this.manualR.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.manualR.DecimalPlaces = 2;
+			this.manualR.Location = new System.Drawing.Point(193, 58);
+			this.manualR.Maximum = new decimal(new int[] {
+            182,
+            0,
+            0,
+            0});
+			this.manualR.Name = "manualR";
+			this.manualR.Size = new System.Drawing.Size(56, 20);
+			this.manualR.TabIndex = 31;
+			// 
+			// manualTheta
+			// 
+			this.manualTheta.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.manualTheta.DecimalPlaces = 2;
+			this.manualTheta.Location = new System.Drawing.Point(193, 84);
+			this.manualTheta.Maximum = new decimal(new int[] {
+            360,
+            0,
+            0,
+            0});
+			this.manualTheta.Minimum = new decimal(new int[] {
+            360,
+            0,
+            0,
+            -2147483648});
+			this.manualTheta.Name = "manualTheta";
+			this.manualTheta.Size = new System.Drawing.Size(56, 20);
+			this.manualTheta.TabIndex = 33;
+			// 
+			// thetaLabel
+			// 
+			this.thetaLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+			this.thetaLabel.AutoSize = true;
+			this.thetaLabel.Location = new System.Drawing.Point(167, 86);
+			this.thetaLabel.Name = "thetaLabel";
+			this.thetaLabel.Size = new System.Drawing.Size(20, 13);
+			this.thetaLabel.TabIndex = 32;
+			this.thetaLabel.Text = "θ °";
 			// 
 			// AnalogStick
 			// 
-			this.AnalogStick.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
 			this.AnalogStick.BackColor = System.Drawing.Color.Gray;
 			this.AnalogStick.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
 			this.AnalogStick.ClearCallback = null;
 			this.AnalogStick.Location = new System.Drawing.Point(3, 3);
 			this.AnalogStick.Name = "AnalogStick";
 			this.AnalogStick.ReadOnly = false;
-			this.AnalogStick.Size = new System.Drawing.Size(129, 129);
+			this.AnalogStick.Size = new System.Drawing.Size(164, 164);
 			this.AnalogStick.TabIndex = 0;
 			this.AnalogStick.X = 0;
 			this.AnalogStick.Y = 0;
@@ -176,6 +230,10 @@
 			// 
 			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
 			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+			this.Controls.Add(this.manualTheta);
+			this.Controls.Add(this.thetaLabel);
+			this.Controls.Add(this.manualR);
+			this.Controls.Add(this.rLabel);
 			this.Controls.Add(this.MaxYNumeric);
 			this.Controls.Add(this.MaxXNumeric);
 			this.Controls.Add(this.MaxLabel);
@@ -185,12 +243,14 @@
 			this.Controls.Add(this.XLabel);
 			this.Controls.Add(this.AnalogStick);
 			this.Name = "VirtualPadAnalogStick";
-			this.Size = new System.Drawing.Size(204, 136);
+			this.Size = new System.Drawing.Size(253, 172);
 			this.Load += new System.EventHandler(this.VirtualPadAnalogStick_Load);
 			((System.ComponentModel.ISupportInitialize)(this.ManualX)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.ManualY)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.MaxXNumeric)).EndInit();
 			((System.ComponentModel.ISupportInitialize)(this.MaxYNumeric)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.manualR)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.manualTheta)).EndInit();
 			this.ResumeLayout(false);
 			this.PerformLayout();
 
@@ -206,5 +266,9 @@
 		private System.Windows.Forms.Label MaxLabel;
 		private System.Windows.Forms.NumericUpDown MaxXNumeric;
 		private System.Windows.Forms.NumericUpDown MaxYNumeric;
+		private System.Windows.Forms.Label rLabel;
+		private System.Windows.Forms.NumericUpDown manualR;
+		private System.Windows.Forms.NumericUpDown manualTheta;
+		private System.Windows.Forms.Label thetaLabel;
 	}
 }
diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.cs
index edd6b6cd02..f562c323c0 100644
--- a/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.cs
+++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.cs
@@ -5,18 +5,34 @@ using System.Windows.Forms;
 using BizHawk.Emulation.Common;
 using BizHawk.Client.Common;
 using System.Windows;
+using BizHawk.Common.NumberExtensions;
 
 namespace BizHawk.Client.EmuHawk
 {
 	public partial class VirtualPadAnalogStick : UserControl, IVirtualPadControl
 	{
+		#region Fields
+
 		private bool _programmaticallyUpdatingNumerics;
 		private bool _readonly;
 
+		private EventHandler manualXYValueChangedEventHandler;
+		private EventHandler polarNumericChangedEventHandler;
+
+		#endregion
+
 		public VirtualPadAnalogStick()
 		{
 			InitializeComponent();
 			AnalogStick.ClearCallback = ClearCallback;
+
+			manualXYValueChangedEventHandler = new EventHandler(ManualXY_ValueChanged);
+			polarNumericChangedEventHandler = new EventHandler(PolarNumeric_Changed);
+
+			ManualX.ValueChanged += manualXYValueChangedEventHandler;
+			ManualY.ValueChanged += manualXYValueChangedEventHandler;
+			manualR.ValueChanged += polarNumericChangedEventHandler;
+			manualTheta.ValueChanged += polarNumericChangedEventHandler;
 		}
 
 		public float[] RangeX = new float[] { -128f, 0.0f, 127f };
@@ -64,6 +80,8 @@ namespace BizHawk.Client.EmuHawk
 		{
 			ManualX.Value = 0;
 			ManualY.Value = 0;
+			manualR.Value = 0;
+			manualTheta.Value = 0;
 			//see HOOMOO
 			Global.AutofireStickyXORAdapter.SetSticky(AnalogStick.XName, false);
 			Global.StickyXORAdapter.Unset(AnalogStick.XName);
@@ -95,12 +113,16 @@ namespace BizHawk.Client.EmuHawk
 						MaxLabel.Enabled =
 						MaxXNumeric.Enabled =
 						MaxYNumeric.Enabled =
+						manualR.Enabled =
+						rLabel.Enabled =
+						manualTheta.Enabled =
+						thetaLabel.Enabled =
 						!value;
 
-					AnalogStick.ReadOnly = 
+					AnalogStick.ReadOnly =
 						_readonly =
 						value;
-				
+
 					Refresh();
 				}
 			}
@@ -130,24 +152,30 @@ namespace BizHawk.Client.EmuHawk
 			AnalogStick.SetPrevious(previous);
 		}
 
-		private void ManualX_ValueChanged(object sender, EventArgs e)
+		private void ManualXY_ValueChanged(object sender, EventArgs e)
 		{
 			SetAnalogControlFromNumerics();
 		}
-
-		private void ManualX_KeyUp(object sender, KeyEventArgs e)
+		private void MaxManualXY_ValueChanged(object sender, EventArgs e)
 		{
-			SetAnalogControlFromNumerics();
+			SetAnalogMaxFromNumerics();
 		}
 
-		private void ManualY_KeyUp(object sender, KeyEventArgs e)
+		private void PolarNumeric_Changed(object sender, EventArgs e)
 		{
-			SetAnalogControlFromNumerics();
-		}
+			ManualX.ValueChanged -= manualXYValueChangedEventHandler;
+			ManualY.ValueChanged -= manualXYValueChangedEventHandler;
 
-		private void ManualY_ValueChanged(object sender, EventArgs e)
-		{
-			SetAnalogControlFromNumerics();
+			ManualX.Value = Math.Ceiling(manualR.Value * (decimal)Math.Cos(Math.PI * (double)manualTheta.Value / 180)).Clamp(-127, 127);
+			ManualY.Value = Math.Ceiling(manualR.Value * (decimal)Math.Sin(Math.PI * (double)manualTheta.Value / 180)).Clamp(-127, 127);
+
+			AnalogStick.X = (int)ManualX.Value;
+			AnalogStick.Y = (int)ManualY.Value;
+			AnalogStick.HasValue = true;
+			AnalogStick.Refresh();
+
+			ManualX.ValueChanged += manualXYValueChangedEventHandler;
+			ManualY.ValueChanged += manualXYValueChangedEventHandler;
 		}
 
 		private void SetAnalogControlFromNumerics()
@@ -188,6 +216,15 @@ namespace BizHawk.Client.EmuHawk
 					ManualY.Value = 0;
 				}
 			}
+
+			manualR.ValueChanged -= polarNumericChangedEventHandler;
+			manualTheta.ValueChanged -= polarNumericChangedEventHandler;
+
+			manualR.Value = (decimal)Math.Sqrt(Math.Pow(AnalogStick.X, 2) + Math.Pow(AnalogStick.Y, 2));
+			manualTheta.Value = (decimal)(Math.Atan2(AnalogStick.Y, AnalogStick.X) * (180 / Math.PI));
+
+			manualR.ValueChanged += polarNumericChangedEventHandler;
+			manualTheta.ValueChanged += polarNumericChangedEventHandler;
 		}
 
 		private void AnalogStick_MouseDown(object sender, MouseEventArgs e)
@@ -210,26 +247,6 @@ namespace BizHawk.Client.EmuHawk
 			}
 		}
 
-		private void MaxXNumeric_ValueChanged(object sender, EventArgs e)
-		{
-			SetAnalogMaxFromNumerics();
-		}
-
-		private void MaxXNumeric_KeyUp(object sender, KeyEventArgs e)
-		{
-			SetAnalogMaxFromNumerics();
-		}
-
-		private void MaxYNumeric_ValueChanged(object sender, EventArgs e)
-		{
-			SetAnalogMaxFromNumerics();
-		}
-
-		private void MaxYNumeric_KeyUp(object sender, KeyEventArgs e)
-		{
-			SetAnalogMaxFromNumerics();
-		}
-
 		private void SetAnalogMaxFromNumerics()
 		{
 			if (!_programmaticallyUpdatingNumerics)
diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/components/AnalogSticklPanel.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/components/AnalogSticklPanel.cs
index 77da016124..46d17aa5c7 100644
--- a/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/components/AnalogSticklPanel.cs
+++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/components/AnalogSticklPanel.cs
@@ -123,8 +123,8 @@ namespace BizHawk.Client.EmuHawk
 
 		//dont count on this working. it's never been tested.
 		//but it kind of must be, or else nothing here would work...
-		public float ScaleX = 0.5f;
-		public float ScaleY = 0.5f;
+		public float ScaleX = 0.60f;
+		public float ScaleY = 0.60f;
 
 		int MinX { get { return (int)(RangeX[0]); } }
 		int MinY { get { return (int)(RangeY[0]); } }
@@ -252,7 +252,7 @@ namespace BizHawk.Client.EmuHawk
 			unchecked
 			{
 				// Background
-				e.Graphics.Clear(Color.Black);
+				e.Graphics.Clear(Color.LightGray);
 
 				e.Graphics.FillRectangle(GrayBrush, PixelMinX, PixelMinY, PixelMaxX - PixelMinX, PixelMaxY- PixelMinY);
 				e.Graphics.FillEllipse(ReadOnly ? OffWhiteBrush : WhiteBrush, PixelMinX, PixelMinY, PixelMaxX - PixelMinX - 2, PixelMaxY - PixelMinY - 3);
diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/N64Schema.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/N64Schema.cs
index 6afa6fb363..aa3ecbfa1d 100644
--- a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/N64Schema.cs
+++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/N64Schema.cs
@@ -26,7 +26,7 @@ namespace BizHawk.Client.EmuHawk
 			return new PadSchema
 			{
 				IsConsole = false,
-				DefaultSize = new Size(220, 316),
+				DefaultSize = new Size(275, 316),
 				Buttons = new[]
 				{
 					new PadSchema.ButtonScema
@@ -34,7 +34,7 @@ namespace BizHawk.Client.EmuHawk
 						Name = "P" + controller + " DPad U",
 						DisplayName = "",
 						Icon = Properties.Resources.BlueUp,
-						Location = new Point(24, 195),
+						Location = new Point(24, 230),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
@@ -42,7 +42,7 @@ namespace BizHawk.Client.EmuHawk
 						Name = "P" + controller + " DPad D",
 						DisplayName = "",
 						Icon = Properties.Resources.BlueDown,
-						Location = new Point(24, 216),
+						Location = new Point(24, 251),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
@@ -50,7 +50,7 @@ namespace BizHawk.Client.EmuHawk
 						Name = "P" + controller + " DPad L",
 						DisplayName = "",
 						Icon = Properties.Resources.Back,
-						Location = new Point(3, 207),
+						Location = new Point(3, 242),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
@@ -58,77 +58,77 @@ namespace BizHawk.Client.EmuHawk
 						Name = "P" + controller + " DPad R",
 						DisplayName = "",
 						Icon = Properties.Resources.Forward,
-						Location = new Point(45, 207),
+						Location = new Point(45, 242),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " L",
 						DisplayName = "L",
-						Location = new Point(3, 150),
+						Location = new Point(3, 185),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " R",
 						DisplayName = "R",
-						Location = new Point(191, 150),
+						Location = new Point(191, 185),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " Z",
 						DisplayName = "Z",
-						Location = new Point(81, 234),
+						Location = new Point(81, 269),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " Start",
 						DisplayName = "S",
-						Location = new Point(81, 211),
+						Location = new Point(81, 246),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " B",
 						DisplayName = "B",
-						Location = new Point(127, 211),
+						Location = new Point(127, 246),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " A",
 						DisplayName = "A",
-						Location = new Point(138, 234),
+						Location = new Point(138, 269),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " C Up",
 						Icon = Properties.Resources.YellowUp,
-						Location = new Point(173, 175),
+						Location = new Point(173, 210),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " C Down",
 						Icon = Properties.Resources.YellowDown,
-						Location = new Point(173, 196),
+						Location = new Point(173, 231),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " C Left",
 						Icon = Properties.Resources.YellowLeft,
-						Location = new Point(152, 189),
+						Location = new Point(152, 221),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
 					{
 						Name = "P" + controller + " C Right",
 						Icon = Properties.Resources.YellowRight,
-						Location = new Point(194, 189),
+						Location = new Point(194, 221),
 						Type = PadSchema.PadInputType.Boolean
 					},
 					new PadSchema.ButtonScema
diff --git a/BizHawk.Common/Extensions/NumberExtensions.cs b/BizHawk.Common/Extensions/NumberExtensions.cs
index cb2f5f19b6..3cb3294de2 100644
--- a/BizHawk.Common/Extensions/NumberExtensions.cs
+++ b/BizHawk.Common/Extensions/NumberExtensions.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System;
+using System.Linq;
 
 namespace BizHawk.Common.NumberExtensions
 {
@@ -106,5 +107,29 @@ namespace BizHawk.Common.NumberExtensions
 		{
 			return a - (b * (int)System.Math.Floor((float)a / b));
 		}
+
+		/// <summary>
+		/// Force the value to be stricly between min and max (both exclued)
+		/// </summary>
+		/// <typeparam name="T">Anything that implements <see cref="IComparable{T}"/></typeparam>
+		/// <param name="val">Value that will be clamped</param>
+		/// <param name="min">Minimum allowed</param>
+		/// <param name="max">Maximum allowed</param>
+		/// <returns>The value if strictly between min and max; otherwise min (or max depending of what is passed)</returns>
+		public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
+		{
+			if(val.CompareTo(min) < 0)
+			{
+				return min;
+			}
+			else if(val.CompareTo(max) > 0)
+			{
+				return max;
+			}
+			else
+			{
+				return val;
+			}
+		}
 	}
 }