パスワードを忘れた? アカウント作成
13457082 journal
日記

iMymeの日記: HSBからRGBへの変換 4

日記 by iMyme

ちょっとC#WPFでHSBからRGBへ変換する必要性が出たのですが、
ネットにあるアプローチはif文とかswitch文で条件分岐させるものばかりだったので、
条件分岐させずにやってみました。

if文で入れ子になるのが嫌だとか、if文使えない言語(意外と存在するらしい)とかにいいのかもしれません。

コードはこちら(Gist)。

詳しい解説等は私のサイトにあります。

この議論は、iMyme (46636)によって ログインユーザだけとして作成されたが、今となっては 新たにコメントを付けることはできません。
  • by minet (45149) on 2017年11月17日 18時39分 (#3314318) 日記

    リストやらLinqやらを持ち出すはちょいとやりすぎでは…
    コーディングサンプルとしてはとても面白いのですが。

    ってことで、もうちょい簡潔な処理を書いてみましたが、いかがでしょう?

    Color GetColorFromHsv(double h, double s, double v)
    {
        double hue6 = h / 60.0; // 6.0 * (h / 360.0)
        double saturation = s / 100.0;
        double value = v / 100.0;

        double amplitude = saturation * value;
        double background = value - amplitude;

        double red = Math.Min(Math.Max(Math.Max(-hue6 + 2.0, hue6 - 4.0), 0.0), 1.0) * amplitude + background;
        double green = Math.Min(Math.Max(Math.Min(hue6, -hue6 + 4.0), 0.0), 1.0) * amplitude + background;
        double blue = Math.Min(Math.Max(Math.Min(hue6 - 2.0, -hue6 + 6.0), 0.0), 1.0) * amplitude + background;

        return Color.FromRgb((byte)(red * byte.MaxValue), (byte)(green * Byte.MaxValue), (byte)(blue * Byte.MaxValue));
    }

    色相→RGB成分のグラフをプロットするようなイメージです。
    域外となる部分をMinとMaxでクリップすることで分岐を省いています。
    計算式を単純化するため、色相の係数が1になるよう1周=6.0とする値(hue6)を用いています。
    彩度と明度からRGB成分の振幅(乗算)とグレー成分のバックグラウンド(加算)を算出しています。

    • なるほど。確かに行数は少ないですね。
      ただ、ぱっと見の可読性が低いので元のロジック
      (根本の導出ロジックはおそらくWikipediaにあるものがベースですよね?)
      を理解するのに時間がかかるのがネックかなという気はします。

      ListやLinqは正直使わないとC#である利点を感じないので、わりとよく使ってます。
      特にListは配列よりも扱いが楽で良いですよ。大きさを気にしなくてもいいですし。
      もちろん日記にあるようなif文もないような言語だと、Linqはおろかfor系すら怪しいので、ご提示されたコードのほうがその場合は適切かもしれませんね。

      もっとも、今回このコードを提示したのはif文を使わないというよりも、むしろ解説にあるような根本のロジックに着目してほしかったというのが大きいですかね。
      まあ、得られる結果は別に変わらないんですけれども、なんかWikipedia流のロジックは釈然としなかったものでして。まああれはあれで行列で考えれば数学的にまとまったのかもしれませんが……。

      親コメント
      • ロジックはこちらのサイト [peko-step.com]の下の方にある「RGB値を求める」を参考に、式を整理したものです。

        可読性については、
        一次方程式(ax+bの式)を見てxy平面上にナナメの直線が想像できるタイプの人で、彩度が100の時の色相に対するR,G,Bの値を知っている(方眼紙にグラフを描ける)なら、理解できるかと思います。
        R,G,Bの上がり下がりのうち上がりの直線と下がりの直線のどちらを採用するかでMin,Maxを使っているところがトリッキーですが(というかハックです。上がり下がりが2回以上あったらこの書き方はできない)。

        なお外側2つのMinとMaxは、.Netに値を0〜1にクリッピングする関数がないためにそう書いているだけなので、clip関数を書いて
        red = clip(Math.Min(-hue6 + 2.0, hue6 - 4.0)) * amplitude + background;
        こんな感じに整理してもよいと思います。

        Wikipediaのコードは非常に手続き的というか中間状態を持ち過ぎで、好きじゃないですね。

        親コメント
typodupeerror

ソースを見ろ -- ある4桁UID

読み込み中...