Как я могу создать "случайный постоянный цвет" для данной строки?


Как я могу генерировать "случайный постоянный цвет" для данной строки во время выполнения?

Таким образом, заданное строковое значение всегда будет иметь один и тот же цвет, но разные строки будут иметь разные цвета.

Например, как gmail присваивает цвета именам отправителей.

Спасибо

Ответы на комментарии:

  1. думая сгенерировать цвет из хэш-кода.
  2. цвета не сохраняются, а генерируются из хэш-кода.
4   3   2015-10-07 17:03:41

4 ответа:

Аналогично тому, что предлагают другие ответы (хэшируйте строку в некоторой форме, а затем используйте этот хэш для выбора цвета), но вместо того, чтобы использовать хэш для прямого вычисления цвета, используйте его в качестве индекса к массиву "приемлемых" цветов.

class ColorPicker
{
    public ColorPicker(int colorCount)
    {
        //The ".Skip(2)" makes it skip pure white and pure black.
        // If you want those two, take out the +2 and the skip.
        _colors = ColorGenerator.Generate(colorCount + 2).Skip(2).ToArray();
    }
    private readonly Color[] _colors;

    public Color StringToColor(string message)
    {
        int someHash = CalculateHashOfStringSomehow(message);
        return _colors[someHash % _colors.Length];
    }

    private int CalculateHashOfStringSomehow(string message)
    {
        //TODO: I would not use "message.GetHashCode()" as you are not
        // guaranteed the same value between runs of the program.
        // Make up your own algorithom or use a existing one that has a fixed 
        // output for a given input, like MD5.
    }
}
Это предотвращает такие проблемы, как получение белого цвета, когда вы планируете показывать текст с белым фоном и другие подобные проблемы.

Чтобы заполнить Ваш Color[] Смотрите этот ответ для класса ColorGenerator или просто сделайте свой собственный заранее определенный список цветов, которые хорошо смотрятся на любом фоне, на котором они будут использоваться.


Приложение:
В случае, если ссылка не работает, вот копия класса ColorGenerator

public static class ColorGenerator
{

    // RYB color space
    private static class RYB
    {
        private static readonly double[] White = { 1, 1, 1 };
        private static readonly double[] Red = { 1, 0, 0 };
        private static readonly double[] Yellow = { 1, 1, 0 };
        private static readonly double[] Blue = { 0.163, 0.373, 0.6 };
        private static readonly double[] Violet = { 0.5, 0, 0.5 };
        private static readonly double[] Green = { 0, 0.66, 0.2 };
        private static readonly double[] Orange = { 1, 0.5, 0 };
        private static readonly double[] Black = { 0.2, 0.094, 0.0 };

        public static double[] ToRgb(double r, double y, double b)
        {
            var rgb = new double[3];
            for (int i = 0; i < 3; i++)
            {
                rgb[i] = White[i]  * (1.0 - r) * (1.0 - b) * (1.0 - y) +
                         Red[i]    * r         * (1.0 - b) * (1.0 - y) +
                         Blue[i]   * (1.0 - r) * b         * (1.0 - y) +
                         Violet[i] * r         * b         * (1.0 - y) +
                         Yellow[i] * (1.0 - r) * (1.0 - b) *        y +
                         Orange[i] * r         * (1.0 - b) *        y +
                         Green[i]  * (1.0 - r) * b         *        y +
                         Black[i]  * r         * b         *        y;
            }

            return rgb;
        }
    }

    private class Points : IEnumerable<double[]>
    {
        private readonly int pointsCount;
        private double[] picked;
        private int pickedCount;

        private readonly List<double[]> points = new List<double[]>();

        public Points(int count)
        {
            pointsCount = count;
        }

        private void Generate()
        {
            points.Clear();
            var numBase = (int)Math.Ceiling(Math.Pow(pointsCount, 1.0 / 3.0));
            var ceil = (int)Math.Pow(numBase, 3.0);
            for (int i = 0; i < ceil; i++)
            {
                points.Add(new[]
                {
                    Math.Floor(i/(double)(numBase*numBase))/ (numBase - 1.0),
                    Math.Floor((i/(double)numBase) % numBase)/ (numBase - 1.0),
                    Math.Floor((double)(i % numBase))/ (numBase - 1.0),
                });
            }
        }

        private double Distance(double[] p1)
        {
            double distance = 0;
            for (int i = 0; i < 3; i++)
            {
                distance += Math.Pow(p1[i] - picked[i], 2.0);
            }

            return distance;
        }

        private double[] Pick()
        {
            if (picked == null)
            {
                picked = points[0];
                points.RemoveAt(0);
                pickedCount = 1;
                return picked;
            }

            var d1 = Distance(points[0]);
            int i1 = 0, i2 = 0;
            foreach (var point in points)
            {
                var d2 = Distance(point);
                if (d1 < d2)
                {
                    i1 = i2;
                    d1 = d2;
                }

                i2 += 1;
            }

            var pick = points[i1];
            points.RemoveAt(i1);

            for (int i = 0; i < 3; i++)
            {
                picked[i] = (pickedCount * picked[i] + pick[i]) / (pickedCount + 1.0);
            }

            pickedCount += 1;
            return pick;
        }

        public IEnumerator<double[]> GetEnumerator()
        {
            Generate();
            for (int i = 0; i < pointsCount; i++)
            {
                yield return Pick();
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public static IEnumerable<Color> Generate(int numOfColors)
    {
        var points = new Points(numOfColors);

        foreach (var point in points)
        {
            var rgb = RYB.ToRgb(point[0], point[1], point[2]);
            yield return Color.FromArgb(
                (int)Math.Floor(255 * rgb[0]),
                (int)Math.Floor(255 * rgb[1]),
                (int)Math.Floor(255 * rgb[2]));
        }
    }
}

Я не знаю никакого специального метода для этого, но вот простой метод, генерирующий шестнадцатеричные значения с MD5 на основе заданной строки:

using System.Security.Cryptography;
using System.Text;


static string GetColor(string raw)
{
    using (MD5 md5Hash = MD5.Create())
    {
        byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(raw));
        return BitConverter.ToString(data).Replace("-", string.Empty).Substring(0, 6);
    }
}

Примеры:

  1. Example@example.com

    -> 23463B

  2. Info@google.com

    -> 3C9015

  3. Stack@exchange.com

    -> 7CA5E8

Редактировать:

Я не проверил его достаточно, так что вы можете настроить это немного, чтобы получить больше различных и уникальных значений.

Edit2:

Если вы хотите прозрачности, проверьте Этот Вопрос/ответ. Установив Substring в Substring(0,8), вы должны вернуть строку с альфа-каналом.

3 целочисленные переменные, r, g и b.

Выполните цикл через каждый символ в строке с шагом 3 и добавьте код символа.

r += n + 0
g += n + 1
b += n + 2

После цикла возьмите r, g и b по модулю 255 и создайте цвет с помощью Color.FromARGB.

Нет гарантий, что цвет будет красивым, и некоторые строки могут иметь цвета, очень близкие друг к другу.

Я вижу некоторые довольно хорошие answeers, но хотя это могло бы внести свой вклад с небольшим забавным решением для генерации цветов из строки, хэш-версия выглядит как лучший способ пойти, но если это дает кому-то некоторое вдохновение, чтобы bould off, есть на это

        ConsoleKeyInfo ch = new ConsoleKeyInfo();
        while (ch.KeyChar != 'e')
        {
            Console.WriteLine("type string to seed color");
            string s = Console.ReadLine(); // gets text from input, in this case the command line
            double d=0;
            foreach(char cha in s.ToCharArray())
            {
                d=+ (int)cha; // get the value and adds it 
            }
            d= (255/(Math.Pow(0.2,-0.002 *d))); // Generates a seed like value from i where 255 is the maximum. basicly 255/0.2^(-0.002*d)

            int i = Convert.ToInt32(d); //then convets and get rid of the decimels
            Color c = Color.FromArgb(i, i, i);// add a bit more calculation for varieng colers.
            Console.WriteLine(c.Name);
            Console.WriteLine("To Exit press e");
            ch = Console.ReadKey()
       }

Edit1: он определенно нуждается в некотором твикинге, так как чем длиннее строка, тем ярче цвет, но я думаю, что-то может выйти из него с небольшой работой:)