126 lines
4.5 KiB
Julia
126 lines
4.5 KiB
Julia
using Colors
|
|
using StatsBase
|
|
using FixedPointNumbers: N0f8, Normed
|
|
using Memoize
|
|
|
|
StatsBase.pairwise(metric::Colors.DifferenceMetric, colours::Vector{<:Colorant}) =
|
|
pairwise((x, y) -> colordiff(x, y; metric), colours)
|
|
|
|
function colourdistances(metric::Colors.DifferenceMetric, colours::Vector{<:Colorant})
|
|
distmat = pairwise(metric, colours)
|
|
Dict(c => sort([distmat[i, j] => colours[j]
|
|
for j in setdiff(axes(distmat, 1), i)],
|
|
by=first)
|
|
for (i, c) in enumerate(colours))
|
|
end
|
|
|
|
function deconstruct(colour::C) where {C <: Colorant}
|
|
fnames = fieldnames(C)
|
|
getfield.(colour, fnames)
|
|
end
|
|
|
|
deconstruct(colour::RGB{N0f8}) =
|
|
Float64.(getfield.(colour, (:r, :g, :b)))
|
|
|
|
function construct(C::Type{<:Colorant}, fields::Tuple)
|
|
eval(Expr(:new, C, fields...))
|
|
end
|
|
|
|
construct(::Type{RGB{N0f8}}, fields::NTuple{3, Float64}) =
|
|
RGB{N0f8}(fields...)
|
|
|
|
@memoize nearestcolour(metric::Colors.DifferenceMetric, options::Vector{<:Colorant}, colour::Colorant) =
|
|
options[argmin(o -> colordiff(options[o], colour; metric), axes(options, 1))]
|
|
|
|
"""
|
|
Examine various mixings of colours `a` and `b` in an attempt to confirm whether
|
|
there are no other colours from `options` that lie directly between `a` and `b`
|
|
according to `metric`.
|
|
|
|
This is done by bisecting the mix factor and checking if any other colours are
|
|
reported as nearest within `tol` of the crossover point.
|
|
"""
|
|
function bisectadjacency(metric::Colors.DifferenceMetric, a::C, b::C, options::Vector{<:Colorant}; tol=1e-6) where {C <: Colorant}
|
|
af, bf, = deconstruct.((a, b))
|
|
lastmixfactor, mixfactor, mixstep = 1.0, 0.5, 0.25
|
|
while abs(lastmixfactor - mixfactor) > tol
|
|
abf = @. mixfactor * af + (1 - mixfactor) * bf
|
|
ab = construct(C, abf)
|
|
near_ab = nearestcolour(metric, options, ab)
|
|
if near_ab == a
|
|
lastmixfactor, mixfactor = mixfactor, mixfactor + mixstep
|
|
elseif near_ab == b
|
|
lastmixfactor, mixfactor = mixfactor, mixfactor - mixstep
|
|
else
|
|
return false
|
|
end
|
|
mixstep /= 2
|
|
end
|
|
return true
|
|
end
|
|
|
|
function adjacentcolours(metric::Colors.DifferenceMetric, colours::Vector{C}) where {C <: Colorant}
|
|
distlist = colourdistances(metric, colours) |> collect
|
|
adjacencylist = Dict{C, Vector{Pair{Float64, C}}}()
|
|
for (c, others) in distlist
|
|
adjacencylist[c] =
|
|
filter(oth -> let o = last(oth)
|
|
if haskey(adjacencylist, o)
|
|
c ∈ last.(adjacencylist[o])
|
|
else
|
|
bisectadjacency(metric, c, o, colours)
|
|
end
|
|
end, others)
|
|
end
|
|
adjacencylist
|
|
end
|
|
|
|
@memoize function growcolours(metric::Colors.DifferenceMetric, colours::Vector{<:Colorant},
|
|
refmat::Matrix{<:Colorant})
|
|
cmat = Matrix{Union{Colorant, Missing}}(fill(missing, size(refmat)))
|
|
cadj = adjacentcolours(metric, colours)
|
|
for i in CartesianIndices(cmat)
|
|
if ismissing(cmat[i])
|
|
# @info "@ $(i.I)"
|
|
cnearest = nearestcolour(metric, colours, refmat[i])
|
|
cmat[i] = cnearest
|
|
if length(cadj[cnearest]) > 0
|
|
safethreshold = minimum(first.(cadj[cnearest]))/2
|
|
growcolour!(cmat, metric, refmat, cnearest, safethreshold, i)
|
|
end
|
|
end
|
|
end
|
|
Matrix{Colorant}(cmat)
|
|
end
|
|
|
|
function growcolour!(cmat::Matrix{Union{Colorant, Missing}}, metric::Colors.DifferenceMetric,
|
|
refmat::Matrix{<:Colorant}, cnearest::Colorant, safethreshold::Float64,
|
|
initalpos::CartesianIndex{2})
|
|
seeds = [initalpos]
|
|
while !isempty(seeds)
|
|
pos = pop!(seeds)
|
|
surrounding = Ref(pos) .+ CartesianIndex{2}.([(0, 1), (0, -1), (1, 0), (-1, 0)])
|
|
filter!(s -> all((1,1) .<= s.I .<= size(refmat)), surrounding)
|
|
for spos in surrounding
|
|
if ismissing(cmat[spos]) &&
|
|
colordiff(cnearest, refmat[spos]; metric) < safethreshold
|
|
cmat[spos] = cnearest
|
|
push!(seeds, spos)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function colour_grid_hsl(xs, ys, saturation=1)
|
|
[HSL(h, saturation, l)
|
|
for h in range(0, 360, length=xs),
|
|
l in range(0, 1, length=ys)]
|
|
end
|
|
|
|
# function growcolours!(cmat::Matrix, metric::Colors.DifferenceMetric,
|
|
# adjc::Dict{Colorant, Vector{Pair{Float64, Colorant}}}, refmat::Matrix,
|
|
# pos::CartesianIndex{2},
|
|
# lrwrap::Bool=false, udwrap::Bool=false)
|
|
|
|
# end
|