1. ホーム
  2. c#

[解決済み] RGBの色が異なるグラフの生成

2023-03-14 11:10:38

質問

グラフを作成し、異なるデータセットを表示する場合、通常、色でセットを区別するのが良い方法です。つまり、ある線は赤で、次の線は緑というように。問題は、データセットの数が未知の場合、これらの色をランダムに生成する必要があり、しばしば互いに非常に近い色になってしまうことです (たとえば、緑、薄緑など)。

これをどのように解決し、どのようにはっきりと異なる色を生成することが可能かについて、何かアイデアはありますか。

私は、どんな例でも(あなたがより簡単に見つけるなら、例なしで問題と解決策を議論するのは自由です)C#とRGBベースの色であったなら、素晴らしいです。

どのように解決するのですか?

0~255 R、G、Bの3つのカラーチャンネルを持っています。

最初に通過する

0, 0, 255
0, 255, 0
255, 0, 0

次に

0, 255, 255
255, 0, 255
255, 255, 0

そして、2 => 128で割って、もう一度始めます。

0, 0, 128
0, 128, 0
128, 0, 0
0, 128, 128
128, 0, 128
128, 128, 0

2で割る => 64

次は64を128に足す=> 192

に従ってください。

プログラムするのが簡単で、かなりはっきりした色が出ます。

EDIT: コードサンプルのリクエスト

また - グレーが許容される色であれば、以下のように追加パターンを追加します。

255, 255, 255
128, 128, 128 

これらをコードで生成する処理には、いくつかの方法があります。

簡単な方法

もし、一定数以上の色が必要ないことが保証されているのであれば、このパターンに従って色の配列を生成し、それを使用すればよいのです。

    static string[] ColourValues = new string[] { 
        "FF0000", "00FF00", "0000FF", "FFFF00", "FF00FF", "00FFFF", "000000", 
        "800000", "008000", "000080", "808000", "800080", "008080", "808080", 
        "C00000", "00C000", "0000C0", "C0C000", "C000C0", "00C0C0", "C0C0C0", 
        "400000", "004000", "000040", "404000", "400040", "004040", "404040", 
        "200000", "002000", "000020", "202000", "200020", "002020", "202020", 
        "600000", "006000", "000060", "606000", "600060", "006060", "606060", 
        "A00000", "00A000", "0000A0", "A0A000", "A000A0", "00A0A0", "A0A0A0", 
        "E00000", "00E000", "0000E0", "E0E000", "E000E0", "00E0E0", "E0E0E0", 
    };

ハード・ウェイ

必要な色の数がわからない場合、以下のコードではこのパターンを使用して最大 896 色を生成します。(896 = 256 * 7 / 2) 256 はチャネルごとの色空間で、7 つのパターンがあり、1 つの色値のみで区切られた色になる前に停止します。

私はおそらく、このコードで必要以上に大変な作業をしてしまいました。まず、255 で開始し、前述のパターンに従って値を生成する強度ジェネレーターがあります。パターン ジェネレーターは、7 つのカラー パターンをループするだけです。

using System;

class Program {
    static void Main(string[] args) {
        ColourGenerator generator = new ColourGenerator();
        for (int i = 0; i < 896; i++) {
            Console.WriteLine(string.Format("{0}: {1}", i, generator.NextColour()));
        }
    }
}

public class ColourGenerator {

    private int index = 0;
    private IntensityGenerator intensityGenerator = new IntensityGenerator();

    public string NextColour() {
        string colour = string.Format(PatternGenerator.NextPattern(index),
            intensityGenerator.NextIntensity(index));
        index++;
        return colour;
    }
}

public class PatternGenerator {
    public static string NextPattern(int index) {
        switch (index % 7) {
        case 0: return "{0}0000";
        case 1: return "00{0}00";
        case 2: return "0000{0}";
        case 3: return "{0}{0}00";
        case 4: return "{0}00{0}";
        case 5: return "00{0}{0}";
        case 6: return "{0}{0}{0}";
        default: throw new Exception("Math error");
        }
    }
}

public class IntensityGenerator {
    private IntensityValueWalker walker;
    private int current;

    public string NextIntensity(int index) {
        if (index == 0) {
            current = 255;
        }
        else if (index % 7 == 0) {
            if (walker == null) {
                walker = new IntensityValueWalker();
            }
            else {
                walker.MoveNext();
            }
            current = walker.Current.Value;
        }
        string currentText = current.ToString("X");
        if (currentText.Length == 1) currentText = "0" + currentText;
        return currentText;
    }
}

public class IntensityValue {

    private IntensityValue mChildA;
    private IntensityValue mChildB;

    public IntensityValue(IntensityValue parent, int value, int level) {
        if (level > 7) throw new Exception("There are no more colours left");
        Value = value;
        Parent = parent;
        Level = level;
    }

    public int Level { get; set; }
    public int Value { get; set; }
    public IntensityValue Parent { get; set; }

    public IntensityValue ChildA {
        get {
            return mChildA ?? (mChildA = new IntensityValue(this, this.Value - (1<<(7-Level)), Level+1));
        }
    }

    public IntensityValue ChildB {
        get {
            return mChildB ?? (mChildB = new IntensityValue(this, Value + (1<<(7-Level)), Level+1));
        }
    }
}

public class IntensityValueWalker {

    public IntensityValueWalker() {
        Current = new IntensityValue(null, 1<<7, 1);
    }

    public IntensityValue Current { get; set; }

    public void MoveNext() {
        if (Current.Parent == null) {
            Current = Current.ChildA;
        }
        else if (Current.Parent.ChildA == Current) {
            Current = Current.Parent.ChildB;
        }
        else {
            int levelsUp = 1;
            Current = Current.Parent;
            while (Current.Parent != null && Current == Current.Parent.ChildB) {
                Current = Current.Parent;
                levelsUp++;
            }
            if (Current.Parent != null) {
                Current = Current.Parent.ChildB;
            }
            else {
                levelsUp++;
            }
            for (int i = 0; i < levelsUp; i++) {
                Current = Current.ChildA;
            }

        }
    }
}