Цветовая интерполяция между 3 цветами в .NET - PullRequest
7 голосов
/ 06 августа 2009

Я хотел бы плавно интерполировать цвет из Цвета A (назовем его красным) в Цвет C (назовем его зеленым), проходя через цвет B (назовем его желтым), основываясь на значении определенной переменной.

Если переменная = 100, я хочу чистый зеленый. Если переменная = 50, я хочу чистый желтый. Если переменная = 0, я хочу чистый красный.

Я понимаю, что вы можете рассматривать каждый RGB-триплет как координату в 3-мерном пространстве. То, что я ищу, - это быстрый и грязный трюк с линейной интерполяцией, который корректно работает с определенной компоновкой типа .NET Color (отдельные значения для ARGB и т. Д.).

Ответы [ 2 ]

15 голосов
/ 06 августа 2009

Во-первых, вы запрашиваете линейную интерполяцию, но не указываете, что цвет B находится на линии между цветом A и цветом C; это необходимо Во-вторых, вы не указали, но я собираюсь сделать упрощающее предположение, что цвет B является средней точкой линии между цветом A и цветом C; следующий код легко модифицируется, если это не так. Наконец, я изменил ваше предположение, что параметр должен быть целым числом от нуля до ста, чтобы быть двойным числом от нуля до единицы. Код легче написать и легче понять в последнем случае, и он все еще может использоваться с первым (разделите ваши входные данные на сто).

class ColorInterpolator {
    delegate byte ComponentSelector(Color color);
    static ComponentSelector _redSelector = color => color.R;
    static ComponentSelector _greenSelector = color => color.G;
    static ComponentSelector _blueSelector = color => color.B;

    public static Color InterpolateBetween(
        Color endPoint1,
        Color endPoint2,
        double lambda) {
        if (lambda < 0 || lambda > 1) {
            throw new ArgumentOutOfRangeException("lambda");
        }
        Color color = Color.FromRgb(
            InterpolateComponent(endPoint1, endPoint2, lambda, _redSelector),
            InterpolateComponent(endPoint1, endPoint2, lambda, _greenSelector),
            InterpolateComponent(endPoint1, endPoint2, lambda, _blueSelector)
        );

        return color;
    }

    static byte InterpolateComponent(
        Color endPoint1,
        Color endPoint2,
        double lambda,
        ComponentSelector selector) {
        return (byte)(selector(endPoint1)
            + (selector(endPoint2) - selector(endPoint1)) * lambda);
    }
}

Как изменить это, если цвет B не является средней точкой между цветом A и цветом C? Самый простой способ заключается в следующем. Если параметр (то, что я называю «lambda») меньше 0.5, умножьте lambda на два и верните интерполированный цвет между цветом A и цветом B. Если параметр больше 0.5, умножьте lambda на два и вычтите одно (это отобразит [0.5, 1] на [0, 1]) и верните интерполированный цвет между цветом B и цветом C.

Если вам не нравится требование, чтобы цвет B находился на линии между цветом A и цветом C, то вы можете использовать именно ту модификацию, которую я только что описал, для выполнения кусочно-линейной интерполяции между цветами.

Наконец, вы не указали, хотите ли вы интерполировать так называемое альфа-значение («A» в «ARGB»). Приведенный выше код легко модифицируется, чтобы справиться и с этой ситуацией. Добавьте еще один ComponentSelector, определенный как color => color.A, используйте InterpolateComponent для интерполяции этого значения и используйте перегрузку Color.FromArgb(int, int, int, int) Color.FromArgb.

1 голос
/ 29 сентября 2014

Другой способ смешивания цветов с использованием распределения Гаусса, подобного этому (любое количество цветов для диапазона от 0,0 до 1,0, чтобы увеличить смешивание, увеличьте значение sigma_2)

public static Color InterpolateColor(Color[] colors, double x)
{
    double r = 0.0, g = 0.0, b = 0.0;
    double total = 0.0;
    double step = 1.0 / (double)(colors.Length - 1);
    double mu = 0.0;
    double sigma_2 = 0.035;

    foreach (Color color in colors)
    {                
        total += Math.Exp(-(x - mu) * (x - mu) / (2.0 * sigma_2)) / Math.Sqrt(2.0 * Math.PI * sigma_2);
        mu += step;
    }

    mu = 0.0;
    foreach(Color color in colors)
    {                
        double percent = Math.Exp(-(x - mu) * (x - mu) / (2.0 * sigma_2)) / Math.Sqrt(2.0 * Math.PI * sigma_2);
        mu += step;

        r += color.R * percent / total;
        g += color.G * percent / total;
        b += color.B * percent / total;
    }

    return Color.FromArgb(255, (int)r, (int)g, (int)b);
}

Дополнительная информация http://en.wikipedia.org/wiki/Normal_distribution

Образец смешивания 3 цвета:

enter image description here

...