How do I change the outer boundaries of a joystick to different shapes?

I have a working joystick that has default boundaries of a square, and I’ve been able to successfully change the square to a circle using ClampMagnitude, but I’d like to know how to change that boundary to other shapes. Specifically I’d like to change the boundary to an Octagon or a Square rotated 45º so the corners of the square are pointing vertically and horizontally.

If it helps, heres’ my joystick code so far. This is meant for a touchscreen mobile device. As of now, my joystick is still squared but the flat sides of the square are actually curved. Like this but more extreme.

namespace UnityStandardAssets.CrossPlatformInput
{

public class Joystick : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
       {
        public enum AxisOption
        {
            // Options for which axes to use
            Both, // Use both
            OnlyHorizontal, // Only horizontal
            OnlyVertical // Only vertical
        }

        public int MovementRange = 100;
        public AxisOption axesToUse = AxisOption.Both; // The options for the axes that the still will use
        public string horizontalAxisName = "Horizontal"; // The name given to the horizontal axis for the cross platform input
        public string verticalAxisName = "Vertical"; // The name given to the vertical axis for the cross platform input

        Vector3 m_StartPos;
        bool m_UseX; // Toggle for using the x axis
        bool m_UseY; // Toggle for using the Y axis
        CrossPlatformInputManager.VirtualAxis m_HorizontalVirtualAxis; // Reference to the joystick in the cross platform input
        CrossPlatformInputManager.VirtualAxis m_VerticalVirtualAxis; // Reference to the joystick in the cross platform input

        Vector3 m_PointerDownPos;

        void OnEnable()
        {
            CreateVirtualAxes();
        }

        void Start()
        {
            m_StartPos = transform.position;

        }

        void UpdateVirtualAxes(Vector3 value)
        {
            var delta = m_StartPos - value;
            delta.y = -delta.y;
            if (m_UseX)
            {
                delta.x /= MovementRange;
                m_HorizontalVirtualAxis.Update(-delta.x);
            }

            if (m_UseY)
            {
                delta.y /= MovementRange;
                m_VerticalVirtualAxis.Update(delta.y);
            }
        }

        void CreateVirtualAxes()
        {
            // set axes to use
            m_UseX = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyHorizontal);
            m_UseY = (axesToUse == AxisOption.Both || axesToUse == AxisOption.OnlyVertical);

            // create new axes based on axes to use
            if (m_UseX)
            {
                m_HorizontalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(horizontalAxisName);
                CrossPlatformInputManager.RegisterVirtualAxis(m_HorizontalVirtualAxis);
            }
            if (m_UseY)
            {
                m_VerticalVirtualAxis = new CrossPlatformInputManager.VirtualAxis(verticalAxisName);
                CrossPlatformInputManager.RegisterVirtualAxis(m_VerticalVirtualAxis);
            }
        }

        public void OnDrag(PointerEventData data)
        {
            // FIXED: Made this method faster by using 2 floats for delta X and Y instead of a vector3 which is later discarded
            float deltaX = 0;
            float deltaY = 0;

            if (m_UseX)
            {
                deltaX = Mathf.Clamp (data.position.x - m_PointerDownPos.x, -MovementRange, MovementRange); // Take drag position relative to pointerdown position instead of pivot; Use movement range in device units as scaled by canvas scaler
            }

            if (m_UseY)
            {
                deltaY = Mathf.Clamp (data.position.y - m_PointerDownPos.y, -MovementRange, MovementRange); // Take drag position relative to pointerdown position instead of pivot; Use movement range in device units as scaled by canvas scaler
            }
            transform.position = Vector3.ClampMagnitude( new Vector3(deltaX, deltaY, 0), MovementRange) + m_StartPos;
            //transform.position = new Vector3(m_StartPos.x + deltaX, m_StartPos.y + deltaY, m_StartPos.z); // Makes joystick boundary squared
            UpdateVirtualAxes(transform.position);
        }

        public void OnPointerUp(PointerEventData data)
        {
            transform.position = m_StartPos;
            UpdateVirtualAxes(m_StartPos);
        }

        public void OnPointerDown(PointerEventData data) {
            m_PointerDownPos = data.position; // CYLFIX: Remember the exact position of pointerdown so drag can be relative to that and not to pivot
        }

        void OnDisable()
        {
            // remove the joysticks from the cross platform input
            if (m_UseX)
            {
                m_HorizontalVirtualAxis.Remove();
            }
            if (m_UseY)
            {
                m_VerticalVirtualAxis.Remove();
            }
        }
    }
}

For anyone who stumbles upon this in the future, the solution is to create your own clamp function which adds the values rather than squaring them with the standard clampMagnitude function.

Here’s the code for clamping the boundaries to a diamond shape (rotated square):

        //clamps joystick boundaries to diamon shape
        public static Vector3 ClampDiamond(Vector2 vector, float maxLength)
        {
            float diamondSize = Mathf.Abs(vector.x) + Mathf.Abs(vector.y);
            if (diamondSize  > maxLength)
            {
                return vector / diamondSize * maxLength;
            }
            else
            {
                return vector;
            }
        }

Keep in mind that clamping like this won’t allow the joystick to go to full 1 magnitude in all directions. It will go to 1 for up/down/left/right, but for diagonals it will only allow a (0.5, 0.5) vector, which only has a magnitude of 0.707…

I have yet to figure out how to clamp the boundaries into an Octagon.