Closed5

VimScriptでRGB-HSL変換する

leviosa42leviosa42

浮動小数点を含む配列で最大値,最小値を求めるときにハマったのでメモ.

-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
leviosa42leviosa42

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
leviosa42leviosa42

書けた.一応拡張性を意識して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={{{,}}}
このスクラップは2024/07/19にクローズされました