Skip to content

Instantly share code, notes, and snippets.

@omnp
Last active July 18, 2024 06:48
Show Gist options
  • Select an option

  • Save omnp/73e5b92628b63c45069d6ed028910e00 to your computer and use it in GitHub Desktop.

Select an option

Save omnp/73e5b92628b63c45069d6ed028910e00 to your computer and use it in GitHub Desktop.
"Fake real" color mixing from RGB values, just something to play with (new revision updated).
import math
import matplotlib.pyplot as plot
EPS = 1e-24
red = (2.0,0.0,0.0)
green = (0.0,2.0,0.0)
blue = (0.0,0.0,2.0)
yellow = (1.0,1.0,0.0)
cyan = (0.0,1.0,1.0)
magenta = (1.0,0.0,1.0)
wl = {"red": 700.0, "orange": 600.0, "yellow": 580.0, "green": 550.0, "cyan": 500.0, "blue": 450.0, "violet": 400.0}
wlr = wl["red"]
wlg = wl["green"]
wlb = wl["blue"]
wly = wl["yellow"]
wlo = wl["orange"]
wlv = wl["violet"]
def interpolate(x,y,f):
t = type(x)
if t in {tuple,list}:
return t(g*(1.-f)+f*h for g,h in zip(x,y))
else:
return x*(1.-f)+f*y
def wavelength_to_rgb(l, gray=0.0, lum=None):
if l > wlr or l < 0.0:
l = l % wlr
if l <= wlr:
res = None
if l > wlg:
f = (l - wlg)/(wlr - wlg)
res = interpolate(green,red,f)
elif l > wlb:
f = (l - wlb)/(wlg - wlb)
res = interpolate(blue,green,f)
else:
f = l/wlb
res = interpolate(red,blue,f)
if lum is None:
res = add_gray(res, gray)
else:
s = (lum - 3*gray)/sum(res)
res = add_gray(scale(res, s), gray)
return res
raise ValueError
def rgb_to_wavelength(x):
mn = min(x)
lum = sum(x)
i = min(range(len(x)),key=lambda i: x[i])
x = sub_gray(x, mn)
if i == 0:
if x[1] == 0:
return wlb, mn, lum
elif x[2] == 0:
return wlg, mn, lum
l = 0.5 * (wlb + wlg)
d = 0.5 * (wlg - wlb)
if i == 1:
if x[2] == 0:
return wlr, mn, lum
if x[0] == 0:
return wlb, mn, lum
l = 0.5 * wlb
d = 0.25 * wlb
if i == 2:
if x[0] == 0:
return wlg, mn, lum
if x[1] == 0:
return wlr, mn, lum
l = 0.5 * (wlr + wlg)
d = 0.5 * (wlr - wlg)
while d > EPS:
y = wavelength_to_rgb(l + d, mn, lum)
err = math.sqrt(sum((e-f)**2 for e,f in zip(x,y)))
yl = wavelength_to_rgb(l - d, mn, lum)
lerr = math.sqrt(sum((e-f)**2 for e,f in zip(x,yl)))
if lerr > err:
l += d
elif err > lerr:
l -= d
d *= 0.5
return l, mn, lum
def inverse(x, unit = 1.0):
t = type(x)
return t(unit - e for e in x)
def sub_gray(x, l = 0.5):
t = type(x)
return t(e - l for e in x)
def add_gray(x, l = 0.5):
t = type(x)
return t(e + l for e in x)
def add(x,y):
t = type(x)
return t(e+f for e,f in zip(x,y))
def sub(x,y):
t = type(x)
return t(e-f for e,f in zip(x,y))
def prod(xs):
if not xs:
raise ValueError
r = None
for x in xs:
if r is None:
r = x
else:
r *= x
return r
def spectra(ls, n=800, width=100.0):
xs = list(range(n))
xs = [e/(n-1) for e in xs]
delta = wlr - wlv
assert(delta == 300.0)
spect = []
for l in ls:
lw = None
if l >= wlv:
lw = tuple(math.pow(2.0*math.pi, -(((delta*e+wlv - l)/(width))**8)) for e in xs)
else:
lw = tuple(1.0 - math.pow(2.0*math.pi, -(((delta*e-wlv + l)/(width))**8)) for e in xs)
spect.append(lw)
return spect, [delta*x+wlv for x in xs]
def absorb(spect, ws, light):
n = len(spect)
assert(len(ws) == n)
res = list(light)
for s,w in zip(spect,ws):
res = multiply(res, inverse(scale(s, w), unit=1.0))
return res
def non_negative(x):
t = type(x)
return t(max(0.0, e) for e in x)
def scale(x, f):
t = type(x)
return t(f*e for e in x)
def multiply(x,y):
t = type(x)
return t(e*f for e,f in zip(x,y))
def trap_integral_tuple(xs, ys):
n = len(xs)
result = [0.0,] * len(ys[0])
previous_y = None
previous_x = None
for x,y in zip(xs,ys):
if previous_x is not None:
result = add(result, tuple(0.5*(e+f)*(x-previous_x)/n for e,f in zip(previous_y, y)))
previous_x = x
previous_y = y
return tuple(result)
def reflected_to_rgb(reflected, xs):
xs, reflected = list(range(len(xs))), list(scale(wavelength_to_rgb(x), r) for x,r in zip(xs,reflected))
return trap_integral_tuple(xs, reflected)
def complementary_wavelengths(x):
[s],l = spectra([x], n=N)
s = inverse(s, unit=1.0)
i = min(range(len(s)),key=lambda i: s[i])
u = rgb_to_wavelength(reflected_to_rgb(s[:i], l[:i]))[0]
v = rgb_to_wavelength(reflected_to_rgb(s[i:], l[i:]))[0]
w = u,v
return w
N = 800
white = [1.0 for _ in range(N)]
spect, xs = spectra([580.0, 700.0], n=N)
reflected0 = absorb(spect[0:1], [1.0], white)
reflected1 = absorb(spect[1:2], [1.0], white)
reflected = absorb(spect, [0.75, 0.75], white)
inv = inverse(non_negative(reflected_to_rgb(reflected, xs)))
wavel,gray,lum = rgb_to_wavelength(inv)
rwavel,rgray,rlum = rgb_to_wavelength(reflected_to_rgb(reflected, xs))
reflected2 = absorb(spectra([580.0, 700.0, wavel], n=N)[0], [0.333333, 0.333333, 0.333333], white)
reflected3 = absorb(spectra([300.0], n=N)[0], [1.0], white)
reflected3 = non_negative(reflected3)
reflected2 = non_negative(reflected2)
reflected1 = non_negative(reflected1)
reflected0 = non_negative(reflected0)
reflected = non_negative(reflected)
left = plot.Rectangle((0,0),0.2,1,fc=reflected_to_rgb(reflected0, xs))
center = plot.Rectangle((0.2,0),0.2,1,fc=reflected_to_rgb(reflected, xs))
center1 = plot.Rectangle((0.4,0),0.2,1,fc=reflected_to_rgb(reflected2, xs))
center2 = plot.Rectangle((0.6,0),0.2,1,fc=inv)
right = plot.Rectangle((0.8,0),0.2,1,fc=reflected_to_rgb(reflected1, xs))
figure = plot.figure(figsize=(16,8))
subfigures = figure.subfigures(1,2)
subfigures[0].gca().add_patch(left)
subfigures[0].gca().add_patch(center)
subfigures[0].gca().add_patch(center1)
subfigures[0].gca().add_patch(center2)
subfigures[0].gca().add_patch(right)
subplot = subfigures[1].subplots(1,1)
subplot.plot(xs, reflected)
subplot.plot(xs, reflected2)
subplot.plot(xs, reflected3)
plot.show()
c = non_negative(inverse(multiply(scale(yellow,1.0),scale(red,1.0)),unit=1.0))
print(c)
l,g,_ = rgb_to_wavelength(c)
print(l,g)
r,x = spectra([wly,wlr], n=N)
sy,sr = scale(r[0], 1.0), scale(r[1], 2.0)
syr = non_negative(inverse(multiply(sy, sr), unit=1.0))
yr = reflected_to_rgb(syr, x)
l,g,_ = rgb_to_wavelength(yr)
myr = min(yr)
myr = sub_gray(yr, myr)
myr = scale(myr, 1.0/max(myr))
print(l,g)
print(yr, myr)
one = plot.Rectangle((0,0),0.2,1,fc=scale(yellow, 0.5))
two = plot.Rectangle((0.2,0),0.2,1,fc=scale(red, 0.5))
three = plot.Rectangle((0.4,0),0.2,1,fc=yr)
four = plot.Rectangle((0.6,0),0.2,1,fc=myr)
five = plot.Rectangle((0.8,0),0.2,1,fc=c)
figure = plot.figure(figsize=(16,8))
subfigures = figure.subfigures(1,2)
subfigures[0].gca().add_patch(one)
subfigures[0].gca().add_patch(two)
subfigures[0].gca().add_patch(three)
subfigures[0].gca().add_patch(four)
subfigures[0].gca().add_patch(five)
subplot = subfigures[1].subplots(1,1)
subplot.plot(x, r[0], color=scale(yellow, 0.5))
subplot.plot(x, r[1], color=scale(red, 0.5))
subplot.plot(x, syr, color=yr)
plot.show()
print(l)
print(l, complementary_wavelengths(l))
print(580.0, complementary_wavelengths(580.0))
w = complementary_wavelengths(580.0)
s,l = spectra((580.,) + w, n=N)
print(reflected_to_rgb(absorb(s, (1.0,1.0,1.0), white), l))
print(wavelength_to_rgb(rgb_to_wavelength(red)[0]))
print(wavelength_to_rgb(rgb_to_wavelength(yellow)[0]))
print(wavelength_to_rgb(rgb_to_wavelength(blue)[0]))
print(wavelength_to_rgb(rgb_to_wavelength(cyan)[0]))
print(wavelength_to_rgb(rgb_to_wavelength(magenta)[0]))
c,g,l = rgb_to_wavelength((0.25, 0.5, 0.75))
d = wavelength_to_rgb(c,g,l)
print(c,g,l)
print(c,d,0.75/0.5,d[2]/d[1])
f,h,l = rgb_to_wavelength(d)
print(c,f,h,l)
@omnp
Copy link
Author

omnp commented Jul 16, 2024

Example plots
colors-22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment