2
\$\begingroup\$

I'd like to form an algorithmic connections between a particular string and and a particular color, in such that each unique string is always the same color.

My particular use case is for a kiosk where each user should immediately notice if someone else is logged in, so my strings are employee first names. Reinforcing this visually by assigning each user a different color. And yes I know i could just build a list or add a property to the user object but this is more fun. Here's my current approach:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    float r = 0;
    float g = 0;
    float b = 0;
    var name = value as string;
    if (name.IsNullOrEmpty()) return new SolidColorBrush(Colors.Black);
    var pos = 0;

    foreach (var letter in name)
    {
        var index = letter % 32;
        if (pos == 0) r = (float)index / AlphabetLength;
        if (pos == 1) g = (float)index / AlphabetLength;
        b = (float)index / AlphabetLength;
        pos++;
    }

    return new SolidColorBrush(Color.FromScRgb(1f, r, g, b));
}

private const int AlphabetLength = 26;

Kind of came up with this quickly so there are a bunch of weaknesses. What do you think?

\$\endgroup\$
  • 2
    \$\begingroup\$ String.IsNullOrEmpty isn't an extension method. Are you sure this code can be compiled? \$\endgroup\$ – Dmitry Nov 4 '16 at 21:17
  • 2
    \$\begingroup\$ What if you started with a restricted color space to insure visibility, at least. Then the first color could be chosen arbitrarily, based on a hash of the string, or whatever. Successive colors could be chosen to maximize the distance from the colors currently in use in the color space. You might be interested in this as an example of this strategy for palette generation. \$\endgroup\$ – ex nihilo Nov 5 '16 at 4:30
  • \$\begingroup\$ @Dmitry it does compile, I must be using a library that adds an extension method for that \$\endgroup\$ – Michael Nov 5 '16 at 17:08
  • \$\begingroup\$ Two logged users with the same first name will have assigned the same color, right? What about distinguish users based on user IP address. \$\endgroup\$ – Tomáš Paul Nov 6 '16 at 15:06
  • \$\begingroup\$ This is really low tech. The kiosk is always logged as a generic user and because this is behind an access controlled door with only a few users they just select which user they are. For data entry purposes it is too much of a hassle to require them to log in with AD. If we ever had two users with the same first name we'd just add a middle initial or something. \$\endgroup\$ – Michael Nov 7 '16 at 12:44
3
\$\begingroup\$

Code Style

  • Where does the 32 come from? It seems strange to have AlphabetLength assigned as a constant, but 32 is an unexplained magic number. Especially if its value is actually based on the length of the alphabet, you should make it clear what the significance of that number is.

  • The method signature is pretty weird. Looking it up, apparently it's from some IValueConverter interface. You'd be better off extracting everything you have here into a private method which takes a string parameter and returns a SolidColorBrush. Then you can call that from Convert, doing any casting or validation you need there.

  • You have a mix of var and explicit type names. It'd be more consistent to declare your floats as var r = 0f, etc.

  • Your RGB values seem to be based entirely on the first, second and last characters of the string. That means there's really no reason to loop over it, you can just assign them directly

  • index is casted to a float everywhere it's used. It'd be cleaner to have it assigned as a float in the first place.

Algorithm

  • Currently, your names can be any colour at all. That's going to make displaying them hard, because you'll potentially have to modify the background colour to make sure the name reads clearly. You might be better off instead using HSV (Hue, Saturation, Value) colour space with a fixed V, then play with the H and S. It might also be good to use a restricted range of S so you don't end up with lots of washed-out, similar-looking colours

  • Using only the first, second and last characters in a name means you're going to have a lot of clashes, where different names show as the same colour. A different approach might be to do something like name.GetHashCode() to get an integer representing the string. You could then use that to generate the HSV (or RGB) components a few ways, like passing it as a seed to new Random (which will be consistent each time). Colour space is relatively limited, so you'll never get away from clashes altogether, but I think this will make them far less common.

\$\endgroup\$
  • 1
    \$\begingroup\$ The method signature comes from the ValueConverter class. It's WPF and the OP forgot to mention it. I've just added the missing tag. \$\endgroup\$ – t3chb0t Nov 6 '16 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.