Closed5
VimScriptでRGB-HSL変換する

モチベーション
- カラースキーム作りで使う
- [projekt0n/github-nvim-theme]では
Color.lighten()
,Color.darken()
のような関数があり便利そうだった
計算式

浮動小数点を含む配列で最大値,最小値を求めるときにハマったのでメモ.
-echo max([1.0, 1.1, 1.2]) " => E805: Using a Float as a Number
+echo sort([1.0, 1.1, 1.2])[2] " => 1.2
" 配列内の要素が全てNumberかFloatなら 第2引数に'f'を指定したほうがよさそう
+echo sort([1.0, 1.1, 1.2], 'f')[2] " => 1.2
sort({list} [, {how} [, {dict}]])
{how} が 'f' ならすべての要素は数値順にソートされる。すべての
値は数値か浮動小数点数でなければならない。
:h sort()
関数にするほど使うのか分からないけど雑に実装した
function s:max(list)
return sort(a:list, 'f')[len(a:list) - 1]
endfunction
function s:min(list)
return sort(a:list, 'f')[0]
endfunction

s:mod()
も作った.:h fmod()
だと戻り値にexpr1
の符号を使う.
fmod({expr1}, {expr2}) fmod()
{expr1} / {expr2} の余りを返す (割り算が表現できなくても)。
{expr2} が非ゼロなら {expr1} - i * {expr2} の結果を返す (i は
戻り値が {expr1} と同じ符号を持ちその絶対値が {expr2} よりも小
さくなるような値)。{expr2} がゼロならゼロが返る。戻り値の型は
浮動小数点数 (Float)。
{expr1} と {expr2} は浮動小数点数 (Float) か数値 (Number)
でなければならない。
{expr1} または {expr2} が Float または Number でない場合は
0.0 を返す。
例: >
:echo fmod(12.33, 1.22)
< 0.13 >
:echo fmod(-12.33, 1.22)
< -0.13
function! s:mod(dividend, divisor)
let rem = fmod(a:dividend, a:divisor)
return rem < 0.0 ? rem + a:divisor : rem
endfunction

書けた.一応拡張性を意識してf(a, b, c)
ではなくリストを引数にとる(f(list)
)ようにしておいた.
#rrggbb
形式で関数に渡せなかったり値がFloatだったり,そのまま使うにはちょっと使いにくいので後でラッパー関数を書くかもしれない.
function! s:max(list)
return sort(a:list, 'f')[len(a:list) - 1]
endfunction
function! s:min(list)
return sort(a:list, 'f')[0]
endfunction
function! s:mod(dividend, divisor)
let rem = fmod(a:dividend, a:divisor)
return rem < 0.0 ? rem + a:divisor : rem
endfunction
" rgb2hsl([r, g, b])
" Convert RGB to HSL.
" @param r Float [0.0,255.0]
" @param g Float [0.0,255.0]
" @param b Float [0.0,255.0]
" @return [h, s, l]
function! s:rgb2hsl(args) " {{{
let [r, g, b] = map(a:args, {_,n -> n/255.0})
let max = s:max([r, g, b])
let min = s:min([r, g, b])
let chroma = max - min
" hue {{{
" ref: https://en.wikipedia.org/wiki/HSL_and_HSV
if chroma == 0.0
" undefined
let h = 0.0
elseif max == r
" let h = 60.0 * ((g - b) / chroma)
" if h < 0.0
" let h += 360.0
" endif
let h = 60 * s:mod((g - b) / chroma, 6)
elseif max == g
let h = 60.0 * ((b - r) / chroma) + 120.0
elseif max == b
let h = 60.0 * ((r - g) / chroma) + 240.0
else
echoerr 'ERR'
endif
" }}}
" lightless {{{
let l = (max + min) / 2.0
" }}}
" saturation {{{
if (l == 1.0) || (l == 0.0)
let s = 0
else
let s = chroma / (1 - abs(2 * l - 1))
endif
" }}}
return [h, s * 100, l * 100]
endfunction " }}}
" hsl2rgb([h, s, l])
" Convert HSL to RGB.
" @param h Float [0.0,360.0]
" @param s Float [0.0,100.0]
" @param l FLoat [0.0,100.0]
" @return [r, g, b]
function! s:hsl2rgb(args) " {{{
let [h, s, l] = a:args
let [s, l] = [s / 100.0, l / 100.0]
let K = {n -> s:mod(n + (h / 30.0), 12.0)}
let a = s * s:min([l, 1.0 - l])
let F = {n -> (l - a * s:max([-1.0, s:min([K(n) - 3.0, 9.0 - K(n), 1])]))}
return [F(0) * 255.0, F(8) * 255.0, F(4) * 255.0]
endfunction " }}}
" vim: ts=2 et fdm=marker fmr={{{,}}}

gistに上げた
このスクラップは2024/07/19にクローズされました