I wrote the two methods below to automatically select N distinct colors. It works by defining a piecewise linear function on the RGB cube. The benefit of this is you can also get a progressive scale if that's what you want, but when N gets large the colors can start to look similar. I can also imagine evenly subdividing the RGB cube into a lattice and then drawing points. Does anyone know any other methods? I'm ruling out defining a list and then just cycling through it. I should also say I don't generally care if they clash or don't look nice, they just have to be visually distinct.
public static List<Color> pick(int num) {
List<Color> colors = new ArrayList<Color>();
if (num < 2)
return colors;
float dx = 1.0f / (float) (num - 1);
for (int i = 0; i < num; i++) {
colors.add(get(i * dx));
}
return colors;
}
public static Color get(float x) {
float r = 0.0f;
float g = 0.0f;
float b = 1.0f;
if (x >= 0.0f && x < 0.2f) {
x = x / 0.2f;
r = 0.0f;
g = x;
b = 1.0f;
} else if (x >= 0.2f && x < 0.4f) {
x = (x - 0.2f) / 0.2f;
r = 0.0f;
g = 1.0f;
b = 1.0f - x;
} else if (x >= 0.4f && x < 0.6f) {
x = (x - 0.4f) / 0.2f;
r = x;
g = 1.0f;
b = 0.0f;
} else if (x >= 0.6f && x < 0.8f) {
x = (x - 0.6f) / 0.2f;
r = 1.0f;
g = 1.0f - x;
b = 0.0f;
} else if (x >= 0.8f && x <= 1.0f) {
x = (x - 0.8f) / 0.2f;
r = 1.0f;
g = 0.0f;
b = x;
}
return new Color(r, g, b);
}
如果N足够大,你会得到一些相似的颜色。世界上只有这么多。
为什么不把它们均匀地分布在光谱中,像这样:
IEnumerable<Color> CreateUniqueColors(int nColors)
{
int subdivision = (int)Math.Floor(Math.Pow(nColors, 1/3d));
for(int r = 0; r < 255; r += subdivision)
for(int g = 0; g < 255; g += subdivision)
for(int b = 0; b < 255; b += subdivision)
yield return Color.FromArgb(r, g, b);
}
如果您想混合序列,以便相似的颜色不在彼此旁边,您可能会打乱结果列表。
是我想得不够周全吗?
您可以使用HSL颜色模型来创建颜色。
如果你想要的只是不同的色调(可能),以及亮度或饱和度的轻微变化,你可以像这样分配色调:
// assumes hue [0, 360), saturation [0, 100), lightness [0, 100)
for(i = 0; i < 360; i += 360 / num_colors) {
HSLColor c;
c.hue = i;
c.saturation = 90 + randf() * 10;
c.lightness = 50 + randf() * 10;
addColor(c);
}
我认为这个简单的递归算法补充了公认的答案,以产生不同的色调值。我为hsv做了它,但也可以用于其他颜色空间。
它在循环中产生色调,在每个循环中尽可能彼此分离。
/**
* 1st cycle: 0, 120, 240
* 2nd cycle (+60): 60, 180, 300
* 3th cycle (+30): 30, 150, 270, 90, 210, 330
* 4th cycle (+15): 15, 135, 255, 75, 195, 315, 45, 165, 285, 105, 225, 345
*/
public static float recursiveHue(int n) {
// if 3: alternates red, green, blue variations
float firstCycle = 3;
// First cycle
if (n < firstCycle) {
return n * 360f / firstCycle;
}
// Each cycle has as much values as all previous cycles summed (powers of 2)
else {
// floor of log base 2
int numCycles = (int)Math.floor(Math.log(n / firstCycle) / Math.log(2));
// divDown stores the larger power of 2 that is still lower than n
int divDown = (int)(firstCycle * Math.pow(2, numCycles));
// same hues than previous cycle, but summing an offset (half than previous cycle)
return recursiveHue(n % divDown) + 180f / divDown;
}
}
我在这里找不到这种算法。我希望这对你有所帮助,这是我在这里的第一篇文章。
Janus的回答,但更容易读懂。我还稍微调整了配色方案,并在你可以自己修改的地方做了标记
我已经把这个片段直接粘贴到一个jupyter笔记本。
import colorsys
import itertools
from fractions import Fraction
from IPython.display import HTML as html_print
def infinite_hues():
yield Fraction(0)
for k in itertools.count():
i = 2**k # zenos_dichotomy
for j in range(1,i,2):
yield Fraction(j,i)
def hue_to_hsvs(h: Fraction):
# tweak values to adjust scheme
for s in [Fraction(6,10)]:
for v in [Fraction(6,10), Fraction(9,10)]:
yield (h, s, v)
def rgb_to_css(rgb) -> str:
uint8tuple = map(lambda y: int(y*255), rgb)
return "rgb({},{},{})".format(*uint8tuple)
def css_to_html(css):
return f"<text style=background-color:{css}> </text>"
def show_colors(n=33):
hues = infinite_hues()
hsvs = itertools.chain.from_iterable(hue_to_hsvs(hue) for hue in hues)
rgbs = (colorsys.hsv_to_rgb(*hsv) for hsv in hsvs)
csss = (rgb_to_css(rgb) for rgb in rgbs)
htmls = (css_to_html(css) for css in csss)
myhtmls = itertools.islice(htmls, n)
display(html_print("".join(myhtmls)))
show_colors()
我为R写了一个名为qualpalr的包,它是专门为此目的设计的。我建议你看看小插图,看看它是如何工作的,但我会尽量总结要点。
qualpalr在HSL颜色空间(前面在这个线程中描述过)中获取一个颜色规范,将其投射到DIN99d颜色空间(感知上是均匀的),并找到使它们之间的最小距离最大化的n。
# Create a palette of 4 colors of hues from 0 to 360, saturations between
# 0.1 and 0.5, and lightness from 0.6 to 0.85
pal <- qualpal(n = 4, list(h = c(0, 360), s = c(0.1, 0.5), l = c(0.6, 0.85)))
# Look at the colors in hex format
pal$hex
#> [1] "#6F75CE" "#CC6B76" "#CAC16A" "#76D0D0"
# Create a palette using one of the predefined color subspaces
pal2 <- qualpal(n = 4, colorspace = "pretty")
# Distance matrix of the DIN99d color differences
pal2$de_DIN99d
#> #69A3CC #6ECC6E #CA6BC4
#> 6ECC6E 22
#> CA6BC4 21 30
#> CD976B 24 21 21
plot(pal2)