仮想計算機構

IT業界と無縁な派遣社員のブログ

Schemeでカラフルな図形を描く

カラーコードを使っていい感じのグラデーションを作りたいと思っていましたがRGBを順番に変化させたとしてもカラフルな画像は作れなさそうです。ネットで色々探していたところ、HSLなるものを知りました ↓ これだ!
いろんな色の指定方法とカラーコードの仕組み | コトダマウェブ

角度を変化させることで色を少しずつ変化できそうです。
16進数のカラーコードを指定して線や円を描くプログラムは以前作成しました。
グラフィックスの下準備 その1 - 仮想計算機構
グラフィックスの下準備 その2 - 仮想計算機構
線を丁寧に描くよ - 仮想計算機構
円を丁寧に描くよ - 仮想計算機構

RGBさえ与えられれば図形を描く準備はできています。あと必要なのはHSLからRGBへの変換を行う機能だけ。というわけで今回はまず変換を行う関数を実装します。

HSLからRGBを計算する方法については以下を参考にしました。
HLS色空間 - Wikipedia

RGBとHSLの関係は以下のサイトなどでも計算できるので、これで実装した関数が正しいかわかります。
RGBとHSLの相互変換ツールと変換計算式 - PEKO STEP

すでにJavaScriptなどで実装していらっしゃる方がいました。こちらも参考にさせていただきました。
JavaScriptでRGB⇔HSL変換 - キリウ君が読まないノート

さて、コードは以下の通りです。

;;; color.scm
(define (hsl-to-rgb h s l)
    (letrec ((Max (+ l (/ (* s (- 1 (abs (- (* 2 l) 1)))) 2))) 
          (Min (- l (/ (* s (- 1 (abs (- (* 2 l) 1)))) 2)))
          (r 0) (g 0) (b 0) (diff (/ (- Max Min) 60)))
        (begin
            (cond
                ((and (<= 0 h) (< h 60)) (begin (set! r Max) (set! g (+ Min (* diff h))) (set! b Min)))
                ((and (<= 60 h) (< h 120)) (begin (set! r (+ Min (* diff (- 120 h)))) (set! g Max) (set! b Min)))
                ((and (<= 120 h) (< h 180)) (begin (set! r Min) (set! g Max) (set! b (+ Min (* diff (- h 120))))))
                ((and (<= 180 h) (< h 240)) (begin (set! r Min) (set! g (+ Min (* diff (- 240 h)))) (set! b Max)))
                ((and (<= 240 h) (< h 300)) (begin (set! r (+ Min (* diff (- h 240)))) (set! g Min) (set! b Max)))
                ((and (<= 300 h) (< h 360)) (begin (set! r Max) (set! g Min) (set! b (+ Min (* diff (- 360 h))))))
                (else (begin (set! r Max) (set! g Max) (set! b Max))) ;;; Max = Min
            )
            (+ (arithmetic-shift (round->exact (* r 255)) 16) (arithmetic-shift (round->exact (* g 255)) 8) (round->exact (* b 255)))
        )
    )
)

この関数使ってカラフルな線を360本だけ描いてみます。線は円状に配置します。
プログラムは以下の通りです。

(use math.const)
(include "grBMP.scm")
(include "line.scm")
(include "circle.scm")
(include "color.scm")

(define r 150)
(do 
    ((angle 0 (+ angle 1)))
    ((= angle 360))
    (letrec ((theta (/ (* pi angle) 180)) (color (hsl-to-rgb angle 0.9 0.5)))
        (gr_line 320 200 (+ 320 (round->exact (* r (cos theta)))) (+ 200 (round->exact (* r (sin theta)))) color)
    )
)
(gr_BMP "test1.bmp")

上記のプログラムを実行すると同じディレクトリ配下にBMP形式のファイルが生成されるので確認してみましょう。
f:id:riverta1992:20200511201327j:plain
いちおう期待した通りにはできました。
中央の1点から線が無数に出ている形なので、中央から遠ざかるほど線と線の間の隙間が目立つ格好となっています。


もう少し別の角度からカラフルな画像を作ってみます。
色と半径の違う円を同心円状に描くプログラムです。

(use math.const)
(include "grBMP.scm")
(include "line.scm")
(include "circle.scm")
(include "color.scm")

(define r_max 180)
(define n_circle 100)
(do
    ((i 1 (+ i 1)))
    ((> i n_circle))
    (letrec ((r (/ (* i r_max) n_circle)) (color (hsl-to-rgb (/ (* i 359) n_circle) 0.9 0.5)))
        (gr_circle 320 200 (round->exact r) color)
    )
)
(gr_BMP "test2.bmp")

プログラムの実行結果は以下の通りです。
f:id:riverta1992:20200511203648j:plain

相変わらず隙間が気になりますが、最初はこんなもんでしょうか。