Initial commit
This commit is contained in:
commit
a1a0974127
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
[deps]
|
||||
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
|
||||
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
|
||||
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
|
||||
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
|
|
@ -0,0 +1,182 @@
|
|||
module ColourApproximations
|
||||
|
||||
using Colors
|
||||
using GLMakie
|
||||
using Makie.GeometryBasics: Rect, HyperRectangle
|
||||
using Observables
|
||||
|
||||
# Utilities
|
||||
|
||||
const colours3bit = # xterm colours
|
||||
Colorant[colorant"rgb(0, 0, 0)",
|
||||
colorant"rgb(205, 0, 0)",
|
||||
colorant"rgb(0, 205, 0)",
|
||||
colorant"rgb(205, 205, 0)",
|
||||
colorant"rgb(0, 0, 238)",
|
||||
colorant"rgb(205, 0, 205)",
|
||||
colorant"rgb(0, 205, 205)",
|
||||
colorant"rgb(229, 229, 229)"]
|
||||
|
||||
const colours4bit = # xterm again
|
||||
vcat(colours3bit,
|
||||
Colorant[colorant"rgb(127, 127, 127)",
|
||||
colorant"rgb(255, 0, 0)",
|
||||
colorant"rgb(0, 252, 0)",
|
||||
colorant"rgb(255, 255, 0)",
|
||||
colorant"rgb(0, 0, 252)",
|
||||
colorant"rgb(255, 0, 255)",
|
||||
colorant"rgb(0, 255, 255)",
|
||||
colorant"rgb(255, 255, 255)"])
|
||||
|
||||
const colour6cube =
|
||||
Colorant[RGB(r, g, b)
|
||||
for r in range(0, 1, length=6)
|
||||
for g in range(0, 1, length=6)
|
||||
for b in range(0, 1, length=6)]
|
||||
|
||||
const colour24greys = Colorant[RGB(w, w, w) for w in range(0, 1, length=24)]
|
||||
|
||||
const colours8bit = vcat(colours4bit, colour6cube, colour24greys)
|
||||
|
||||
const coloursets =
|
||||
[Symbol("3-bit") => colours3bit,
|
||||
Symbol("4-bit") => colours4bit,
|
||||
Symbol("8-bit") => colours8bit]
|
||||
|
||||
nearestcolour(metric::Colors.DifferenceMetric, options::Vector{<:Colorant}, colour::Colorant) =
|
||||
options[argmin(o -> colordiff(options[o], colour; metric), axes(options, 1))]
|
||||
|
||||
const nearcolourcache =
|
||||
Dict{Colors.DifferenceMetric, Dict{Symbol, Dict{Colorant, Colorant}}}()
|
||||
|
||||
function nearestcolour(metric::Colors.DifferenceMetric, options::Symbol)
|
||||
if !haskey(nearcolourcache, metric)
|
||||
nearcolourcache[metric] = Dict{Symbol, Dict{Colorant, Colorant}}()
|
||||
end
|
||||
if !haskey(nearcolourcache[metric], options)
|
||||
nearcolourcache[metric][options] = Dict{Colorant, Colorant}()
|
||||
end
|
||||
relevantcache = nearcolourcache[metric][options]
|
||||
colourset = coloursets[findfirst(s -> first(s) == options, coloursets)] |> last
|
||||
function (c::Colorant)
|
||||
cached = get(relevantcache, c, nothing)
|
||||
if isnothing(cached)
|
||||
cached = relevantcache[c] = nearestcolour(metric, colourset, c)
|
||||
end
|
||||
cached
|
||||
end
|
||||
end
|
||||
|
||||
# Extra colour differences
|
||||
|
||||
struct DE_RGB <: Colors.EuclideanDifferenceMetric{RGB} end
|
||||
|
||||
struct DE_HSL <: Colors.EuclideanDifferenceMetric{HSL} end
|
||||
|
||||
colour_distance_metrics =
|
||||
["CIEΔE2000" => DE_2000(),
|
||||
"CIEΔE94" => DE_94(),
|
||||
"BFD" => DE_BFD(),
|
||||
"CMC" => DE_CMC(),
|
||||
"JPC79" => DE_JPC79(),
|
||||
"l2 LAB" => DE_AB(),
|
||||
"l2 DIN99" => DE_DIN99(),
|
||||
"l2 DIN99d" => DE_DIN99d(),
|
||||
"l2 DIN99o" => DE_DIN99o(),
|
||||
"l2 RGB" => DE_RGB(),
|
||||
"l2 HSL" => DE_HSL()]
|
||||
|
||||
# The control observables
|
||||
|
||||
saturation = Observable(1.0)
|
||||
|
||||
x_granularity = Observable(256)
|
||||
y_granularity = Observable(128)
|
||||
|
||||
diffmetric = Observable{Colors.DifferenceMetric}(last(first(colour_distance_metrics)))
|
||||
|
||||
# Plot setup
|
||||
|
||||
fig = Figure()
|
||||
|
||||
controlrow1 = fig[1,1] = GridLayout()
|
||||
|
||||
xgrad_slider = Slider(controlrow1[1,2], range=32:32:1024, startvalue=x_granularity[])
|
||||
Label(controlrow1[1,1], text="X divisions")
|
||||
ygrad_slider = Slider(controlrow1[1,4], range=16:16:512, startvalue=y_granularity[])
|
||||
Label(controlrow1[1,3], text="Y divisions")
|
||||
metric_slider = Slider(controlrow1[1,6], range=1:length(colour_distance_metrics))
|
||||
Label(controlrow1[1,5], text=@lift(colour_distance_metrics[$(metric_slider.value)] |> first))
|
||||
|
||||
connect!(x_granularity, xgrad_slider.value)
|
||||
connect!(y_granularity, ygrad_slider.value)
|
||||
|
||||
# colsize!(controlrow1, 1, Auto(true, 0.0))
|
||||
# colsize!(controlrow1, 2, Fixed(120))
|
||||
# colsize!(controlrow1, 3, Auto(true, 0.0))
|
||||
# colsize!(controlrow1, 4, Fixed(120))
|
||||
# colsize!(controlrow1, 5, Auto(true, 0.0))
|
||||
# colsize!(controlrow1, 6, Fixed(160))
|
||||
|
||||
ax = Axis(fig[2,1])
|
||||
hidedecorations!(ax)
|
||||
hidespines!(ax)
|
||||
|
||||
controlrow2 = fig[3,1] = GridLayout()
|
||||
|
||||
saturation_slider = Slider(controlrow2[1,2], range = 0:0.01:1, startvalue=saturation[])
|
||||
Label(controlrow2[1,1], text="Saturation")
|
||||
colourset_slider = Slider(controlrow2[1,4], range=1:length(coloursets))
|
||||
Label(controlrow2[1,3], text=@lift(coloursets[$(colourset_slider.value)] |> first |> string))
|
||||
round_colours = Toggle(controlrow2[1,6])
|
||||
Label(controlrow2[1,5], text="Round")
|
||||
|
||||
colsize!(controlrow2, 1, Auto(true, 0.0))
|
||||
colsize!(controlrow2, 2, Auto(false, 0.8))
|
||||
colsize!(controlrow2, 3, Auto(true, 0.0))
|
||||
colsize!(controlrow2, 4, Fixed(100))
|
||||
colsize!(controlrow2, 5, Auto(true, 0.0))
|
||||
colsize!(controlrow2, 6, Fixed(60))
|
||||
|
||||
# Plotting
|
||||
|
||||
connect!(saturation, saturation_slider.value)
|
||||
|
||||
map!(diffmetric, metric_slider.value) do v
|
||||
colour_distance_metrics[v] |> last
|
||||
end
|
||||
|
||||
roundfun = Observable{Function}(identity)
|
||||
map!(roundfun, diffmetric, colourset_slider.value) do metric, cset
|
||||
nearestcolour(metric, coloursets[cset] |> first)
|
||||
end
|
||||
|
||||
rect_list = Observable(Vector{HyperRectangle}())
|
||||
|
||||
rect_list = map(x_granularity, y_granularity) do x, y
|
||||
[Rect(i, j, 1, 1) for i in 1:x for j in 1:y]
|
||||
end
|
||||
|
||||
colour_list_unprocessed = map(saturation) do s
|
||||
[HSL(h, s, l)
|
||||
for h in range(0, 360, length=x_granularity[])
|
||||
for l in range(0, 1, length=y_granularity[])]
|
||||
end
|
||||
|
||||
colour_list = Observable(Vector{Colorant}())
|
||||
map!(colour_list, colour_list_unprocessed, roundfun, round_colours.active) do clist, cround, croundp
|
||||
if croundp cround.(clist) else clist end
|
||||
end
|
||||
|
||||
# The main event
|
||||
|
||||
p = poly!(ax, rect_list, color = colour_list)
|
||||
|
||||
on(rect_list, priority=1) do
|
||||
delete!(ax, p)
|
||||
p = poly!(ax, rect_list, color = colour_list)
|
||||
end
|
||||
|
||||
fig
|
||||
|
||||
end
|
Loading…
Reference in New Issue