#673 – Mapping Mouse Position to Color

You can map a 2D mouse position to a color by using the mouse position to map to the Hue and Saturation and then converting the HSV value to an RGB value.  (We just use 1.0 as the Value component of HSV).  We can map the mouse to a color wheel and use the angle of a line from the mouse position to the center of the wheel as the hue and use the distance from the center of the wheel as the saturation.

So we want to map a mouse position against a color wheel like the following:

In WPF, we’ll just create a Canvas, with a Label that will be used to dump out the RGB values.

<Window Name="win1" x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Color from Mouse Position"
        SizeToContent="WidthAndHeight"
        MouseMove="win1_MouseMove_1">

    <Canvas x:Name="canv1" Width="400" Height="400">
        <Label Content="{Binding RGBInfo}" HorizontalAlignment="Center" />
    </Canvas>
</Window>

In the code-behind, we’ll handle the MouseMove event for the window and re-calculate the color of the window’s background brush, based on the mouse position.

    /// <summary>
    ///
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        SolidColorBrush backBrush = new SolidColorBrush();

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;
            win1.Background = backBrush;
        }

        private string rgbInfo;
        public string RGBInfo
        {
            get { return rgbInfo; }
            set
            {
                if (value != rgbInfo)
                {
                    rgbInfo = value;
                    RaisePropertyChanged("RGBInfo");
                }
            }
        }

        private void win1_MouseMove_1(object sender, MouseEventArgs e)
        {
            double radius = (canv1.ActualWidth / 2);

            Color c = ColorFromMousePosition(e.GetPosition(canv1), radius);
            backBrush.Color = c;
            RGBInfo = string.Format("R={0}, G={1}, B={2}", c.R, c.G, c.B);
        }

        Color ColorFromMousePosition(Point mousePos, double radius)
        {
            // Position relative to center of canvas
            double xRel = mousePos.X - radius;
            double yRel = mousePos.Y - radius;

            // Hue is angle in deg, 0-360
            double angleRadians = Math.Atan2(yRel, xRel);
            double hue = angleRadians * (180 / Math.PI);
            if (hue < 0)
                hue = 360 + hue;

            // Saturation is distance from center
            double saturation = Math.Min(Math.Sqrt(xRel * xRel + yRel * yRel) / radius, 1.0);

            byte r, g, b;
            ColorUtil.HsvToRgb(hue, saturation, 1.0, out r, out g, out b);
            return Color.FromRgb(r, g, b);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

Notice that we make use of an HSV to RGB conversion function, found at http://www.splinter.com.au/converting-hsv-to-rgb-colour-using-c/. Below is the function as we’re using it.

    public static class ColorUtil
    {
        /// <summary>
        /// Convert HSV to RGB
        /// h is from 0-360
        /// s,v values are 0-1
        /// r,g,b values are 0-255
        /// Based upon http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space#HSV_Transformation_C_.2F_C.2B.2B_Code_2
        /// </summary>
        public static void HsvToRgb(double h, double S, double V, out byte r, out byte g, out byte b)
        {
            // ######################################################################
            // T. Nathan Mundhenk
            // mundhenk@usc.edu
            // C/C++ Macro HSV to RGB

            double H = h;
            while (H < 0) { H += 360; };
            while (H >= 360) { H -= 360; };
            double R, G, B;
            if (V <= 0)
            { R = G = B = 0; }
            else if (S <= 0)
            {
                R = G = B = V;
            }
            else
            {
                double hf = H / 60.0;
                int i = (int)Math.Floor(hf);
                double f = hf - i;
                double pv = V * (1 - S);
                double qv = V * (1 - S * f);
                double tv = V * (1 - S * (1 - f));
                switch (i)
                {

                    // Red is the dominant color

                    case 0:
                        R = V;
                        G = tv;
                        B = pv;
                        break;

                    // Green is the dominant color

                    case 1:
                        R = qv;
                        G = V;
                        B = pv;
                        break;
                    case 2:
                        R = pv;
                        G = V;
                        B = tv;
                        break;

                    // Blue is the dominant color

                    case 3:
                        R = pv;
                        G = qv;
                        B = V;
                        break;
                    case 4:
                        R = tv;
                        G = pv;
                        B = V;
                        break;

                    // Red is the dominant color

                    case 5:
                        R = V;
                        G = pv;
                        B = qv;
                        break;

                    // Just in case we overshoot on our math by a little, we put these here. Since its a switch it won't slow us down at all to put these here.

                    case 6:
                        R = V;
                        G = tv;
                        B = pv;
                        break;
                    case -1:
                        R = V;
                        G = pv;
                        B = qv;
                        break;

                    // The color is not defined, we should throw an error.

                    default:
                        //LFATAL("i Value error in Pixel conversion, Value is %d", i);
                        R = G = B = V; // Just pretend its black/white
                        break;
                }
            }
            r = Clamp((byte)(R * 255.0));
            g = Clamp((byte)(G * 255.0));
            b = Clamp((byte)(B * 255.0));
        }

        /// <summary>
        /// Clamp a value to 0-255
        /// </summary>
        private static byte Clamp(byte i)
        {
            if (i < 0) return 0;
            if (i > 255) return 255;
            return i;
        }
    }

The final result is a window whose background color changes as we move the mouse around.

About Sean
Software developer in the Twin Cities area, passionate about .NET technologies. Equally passionate about my own personal projects related to family history and preservation of family stories and photos.

3 Responses to #673 – Mapping Mouse Position to Color

  1. Pingback: Dew Drop – October 22, 2012 (#1,426) | Alvin Ashcraft's Morning Dew

  2. Bjoern says:

    Dear Sean.
    I have spend a lot of hours to get a running solution with a color picker in c# wpf.
    I’ve had a look into your source, but don’t get any logical color values out 😦
    The source send me RGB values which are position dependent, but not color dependent…
    Is it possible to send you me project with your code which dont work properly?
    My project must be ready in the next time and i have no idea where i must search for the failure…

    Greetings from germany and i hope that you’ll read my comment in the next time…

    Bjoern

    • Sean says:

      Hell Bjoern,

      All of the code that I have is already posted on this site. The code worked fine when I wrote the post, so should work for you as a starting point. So perhaps you could post your code on Stack Overflow and get some assistance on whatever is not working.

      Sean

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: