Интерполяция цвета между 3 цветами in.NET

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

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

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

2 ответов


во-первых, вы просите линейную интерполяцию, но вы не указываете, что цвет 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.


другой способ смешивания цветов с использованием гауссова распределения, как это (любое количество цветов для диапазона 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