Ruby 06 画像の生成(総合実習)

# 注意

**このファイルを使う際には、必ず同じ階層に「colorName.csv」を配置してください。
配置していない場合、カーラネームでの色の指定ができません。**

# 設定用関数
設定用関数です。
描画には関係ありません。

## init関数
[引数] スクリーンサイズ x座標(デフォルト 1980), スクリーンサイズ y座標(デフォルト 1080)
[説明] 
初期化用関数。draw関数内で必ず最初に使用してください。
デフォルトサイズ 1980x1080

## pset関数
[引数] 点を打つ x座標, 点を打つ y座標, 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明] 
指定した座標に指定した色、透明度で点を打ちます。

## writeimage関数
[引数] 書き込むファイル名
[説明] 
データをppmファイルに書き込みます。ppmファイルのみ指定できます。

# 計算用関数
描画に使う計算をするための関数です。

## deg2rad関数
[引数] 弧度法に変換したい角度(度数法)
[説明] 
度数法(deg)を弧度法(rad)に変換します。
Math.sin, Math.cosなどの引数に度数法を指定したいときに使用します。

## getColor関数
[引数] カラーコード(文字列)
[説明]
カラーコードもしくはカラーネームをrgbに変換します。「#ffffff」のような形で指定してください。
カラーネームについては
https://www.webcreatorbox.com/webinfo/color-name
を参照してください。

# 描画用関数
図形を描画するための関数です。

## background関数
[引数] 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
背景を描画します。

## line関数
[引数] 開始 x座標, 開始 y座標, 終点 x座標, 終点 y座標, 幅, 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
線を描画します。

## circle関数
[引数] 中心 x座標, 中心 y座標, 半径, 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
塗りつぶされた円を描画します。
枠線を描画したい場合はdonut関数を使用して下さい。

## ellipse関数
[引数] 中心 x座標, 中心 y座標, 横(x軸方向)の半径, 縦(y軸方向)の半径, 回転角度(度数法)(デフォルト 0.0), 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
塗りつぶされた楕円を描画します。
回転角度には度数法を指定します。

## donut関数
[引数] 中心 x座標, 中心 y座標, 半径1, 半径2 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
ドーナツ型を描画します。
半径2 < 半径1となるように引数を指定してください


## triangle関数
[引数] 頂点 x座標1, 頂点 y座標1, 頂点 x座標2, 頂点 y座標2, 頂点 x座標3, 頂点 y座標3, 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
三角形を描画します。

## rect関数
[引数] 中心 x座標, 中心 y座標, 幅, 高さ, 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
長方形を描画します。

## trapezoid関数
[引数] 中心 x座標, 中心 y座標, 上底の長さ, 下底の長さ, 高さ, 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
台形を描画します。

## rhombus関数
[引数] 中心 x座標, 中心 y座標, 幅, 高さ, 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
ひし形を描画します。

## star関数
[引数] 中心 x座標, 中心 y座標, 外接円の半径, 頂点の数, 回転角度(弧度法)(デフォルト 0.0) 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
星形を描画します。
回転角度には弧度法を指定してください。
[参考] http://blog.livedoor.jp/reona396/archives/54602822.html

## polygon関数
[引数] 中心 x座標, 中心 y座標, 外接円の半径, 頂点の数, 回転角度(弧度法)(デフォルト 0.0) 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
正多角形を描画します。
回転角度には弧度法を指定してください。
[参考] http://blog.livedoor.jp/reona396/archives/54602822.html

## convex関数
[引数] 頂点 x座標の配列, 頂点 y座標の配列, 回転角度(弧度法)(デフォルト 0.0) 色(デフォルト 0, 0, 0), 透明度(0.0)
[説明]
凸多角形を描画します。
凹みがある場合は描写できません。

#メイン関数
描画用の処理を書くための関数です。
## draw関数
[説明]
init関数を一番最初に記述してください。
最後にwriteimage関数で画像を描画します。

# ファイルのダウンロード
https://github.com/MiMi1906/UEC

右上の緑の「Code」から「Download ZIP」を選択するとダウンロードできます。

# 全体のプログラム

```ruby:drawImg.rb
# drawImg.rb

#
# ========== settings ==========
#

require "csv"


Pixel = Struct.new(:r, :g, :b)

# init func
# [args] screenSizeX, screenSizeY
def init(screenSizeX = 1980, screenSizeY = 1080)

    # create colorNameArray
    # [Reference] https://www.webcreatorbox.com/webinfo/color-name
    $colorName = CSV.read("colorName.csv")

    # default screenSize
    $screenX = screenSizeX
    $screenY = screenSizeY

    # default var draw area
    $i0 = 0
    $i1 = $screenX
    $j0 = 0
    $j1 = $screenY

    # define goldenRatio
    $goldenRatio = 1.618

    # create imgArray
    $img = Array.new($screenY) do
        Array.new($screenX) do
            Pixel.new(255, 255, 255)
        end
    end
end

# pointSet func
# [args] x, y, color, a
def pset(x, y, color = [255, 255, 255], a = 0.0)
    # error
    if x < 0 || x >= $screenX || y < 0 || y >= $screenY
        return
    end

    r = color[0]
    g = color[1]
    b = color[2]

    $img[y][x].r = ($img[y][x].r * a + r * (1.0 - a)).to_i
    $img[y][x].g = ($img[y][x].g * a + g * (1.0 - a)).to_i
    $img[y][x].b = ($img[y][x].b * a + b * (1.0 - a)).to_i
end

# writeImage func
# [args] filename
def writeimage(name)
    s = "P6\n" + $screenX.to_s + " " + $screenY.to_s + "\n255"
    open(name, "wb") do |f|
        f.puts(s)
        $img.each do |a|
            a.each do |p|
                f.write(p.to_a.pack("ccc"))
            end
        end
    end
    return true
end

#
# ========== calc func ==========
#

# deg => rad func
# [args] theta(deg)
def deg2rad(theta)
    return theta * Math::PI / 180.0
end

# judge isinside func
# [args] x, y, arrayX, arrayY
# [Reliance] oprod
def isinside(x, y, ax, ay)
    (ax.length-1).times do |i|
        if oprod(ax[i+1]-ax[i], ay[i+1]-ay[i], x-ax[i], y-ay[i]) < 0
            return false
        end
    end
    return true
end

# get outer product
# [args] a, b, c, d
def oprod(a, b, c, d)
    return a*d - b*c
end

# getColor (colorCode to rgba)
# [args] colorCode
def getColor(colorCode)
    rgb = Array.new()
    
    # colorName check
    $colorName.each_index do |i|
        if colorCode === $colorName[i][0]
            colorCode = $colorName[i][1]
            break
        end
    end

    # convert colorCode
    if colorCode[0] === "#"
        1.step(6, 2) do |i|
            str = colorCode[i] + colorCode[i+1]
            rgb.push(str.to_i(16))
        end
    else
        rgb.push(0)
        rgb.push(0)
        rgb.push(0)
    end
    return rgb
end

# set draw area func
# [args] x0, x1, y0, y1
def setDrawArea(x0, x1, y0, y1)
    $i0 = x0.to_i
    $i1 = x1.to_i
    $j0 = y0.to_i
    $j1 = y1.to_i
end

#
# ========== draw shape func ==========
#

# draw background func
# [args] color, a
def background(color = [0, 0, 0], a = 0.0)
    $screenY.times do |j|
        $screenX.times do |i|
            pset(i, j, color, a)
        end
    end
end

# draw line
# [args] startX, startY, endX, endY, width, color, a
# [Reliance] convex
def line(x0, y0, x1, y1, w, color = [0, 0, 0], a = 0.0)
    dx = y1 - y0
    dy = x0 - x1
    n = 0.5 * w / Math.sqrt(dx**2 + dy**2)
    dx *= n
    dy *= n
    convex([x0-dx, x0+dx, x1+dx, x1-dx, x0-dx], [y0-dy, y0+dy, y1+dy, y1-dy, y0-dy], color, a)
end

# draw circle func
# [args] centerX, centerY, radius, color, a
def circle(x, y, rad, color = [0, 0, 0], a = 0.0)
    setDrawArea(x-rad, x+rad, y-rad, y+rad)

    $j0.step($j1) do |j|
        $i0.step($i1) do |i|
            if (i - x)**2 + (j - y)**2 < rad**2
                if block_given?
                    yield(i, j)
                else
                    pset(i, j, color, a)
                end
            end
        end
    end
end

# draw ellipse func
# [args] centerX, centerY, radiusX, radiusY, rotateTheta(deg), color, a
def ellipse(x, y, radX, radY, theta = 0.0, color = [0, 0, 0], a = 0.0)
    d = (if radX > radY then radX else radY end)
    setDrawArea(x-d, x+d, y-d, y+d)

    thetaRad = theta * Math::PI / 180
    $j0.step($j1) do |j|
        $i0.step($i1) do |i|
            dx = i - x
            dy = j - y
            px = dx * Math.cos(thetaRad) - dy * Math.sin(thetaRad)
            py = dx * Math.sin(thetaRad) + dy * Math.cos(thetaRad)
            if (px/radX)**2 + (py/radY)**2 < 1.0
                if block_given?
                    yield(i, j)
                else
                    pset(i, j, color, a)
                end
            end
        end
    end
end

# draw donut func
# [args] centerX, centerY, radius1, radius2, color, a
def donut(x, y, rad1, rad2, color = [0, 0, 0], a = 0.0)
    setDrawArea(x-rad1, x+rad1, y-rad1, y+rad1)

    $j0.step($j1) do |j|
        $i0.step($i1) do |i|
            d = (i - x)**2 + (j - y)**2
            if rad2**2 < d && d < rad1**2
                if block_given?
                    yield(i, j)
                else
                    pset(i, j, color, a)
                end
            end
        end
    end
end

# draw triangle func
# [args] x0, y0, x1, y1, x2, y2, color, a
# [Reliance] convex
def triangle(x0, y0, x1, y1, x2, y2, color = [0, 0, 0], a = 0.0)
    convex([x0, x1, x2, x0], [y0, y1, y2, y0], color, a)
    convex([x0, x2, x1, x0], [y0, y2, y1, y0], color, a)
end

# draw rect func
# [args] centerX, centerY, width, height, color, a
def rect(x, y, w, h, color = [0, 0, 0], a = 0.0)
    setDrawArea(x-0.5*w, x+0.5*w, y-0.5*h, y+0.5*h)

    $j0.step($j1) do |j|
        $i0.step($i1) do |i|
            if block_given?
                yield(i, j)
            else
                pset(i, j, color, a)
            end
        end
    end
end

# draw trapezoid func
# [args] centerX, centerY, topLength, bottomLength, height, color, a
# [Reliance] convex
def trapezoid(x, y, u, b, h, color = [0, 0, 0], a = 0.0)
    convex([x+u/2, x+b/2, x-b/2, x-u/2, x+u/2], [y-h/2, y+h/2, y+h/2, y-h/2, y-h/2], color, a)
end

# draw rhombus func
# [args] centerX, centerY, width, height, color, a
# [Reliance] convex
def rhombus(x, y, w, h, color = [0, 0, 0], a = 0.0)
    convex([x, x+w, x, x-w, x], [y-h, y, y+h, y, y-h], color, a)
end

# draw star func
# [args] centerX, centerY, radius, vertexNum, theta, color, a
# [Reliance] convex
# [Reference] http://blog.livedoor.jp/reona396/archives/54602822.html
def star(x, y, rad, vertexNum, theta = 0.0, color = [0, 0, 0], a = 0.0)
    rOut = rad
    rIn = rad / 2.0
    vertexNum *= 2
    (vertexNum/2).times do |i|
        x1 = rOut * Math.cos(deg2rad((360 / vertexNum * i * 2) - 90 + theta).to_f) + x
        x2 = rIn * Math.cos(deg2rad((360 / vertexNum * (i * 2 + vertexNum + 1)) - 90 + theta).to_f) + x
        x3 = x
        x4 = rIn * Math.cos(deg2rad((360 / vertexNum * (i * 2 + vertexNum - 1)) - 90 + theta).to_f) + x
        y1 = rOut * Math.sin(deg2rad((360 / vertexNum * i * 2) - 90 + theta).to_f) + y
        y2 = rIn * Math.sin(deg2rad((360 / vertexNum * (i * 2 + vertexNum + 1)) - 90 + theta).to_f) + y
        y3 = y
        y4 = rIn * Math.sin(deg2rad((360 / vertexNum * (i * 2 + vertexNum - 1)) - 90 + theta).to_f) + y
        convex([x1, x2, x3, x4, x1], [y1, y2, y3, y4, y1], color, a)
    end
end

# draw polygon func
# [args] centerX, centerY, radius, vertexNum, theta, color, a
# [Reliance] convex
# [Reference] http://blog.livedoor.jp/reona396/archives/54602822.html
def polygon(x, y, rad, vertexNum, theta = 0.0, color = [0, 0, 0], a = 0)
    ax = Array.new()
    ay = Array.new()
    vertexNum.times do |i|
        ax.push(rad * Math.cos(deg2rad((360 / vertexNum * i) - 90).to_f) + x)
        ay.push(y1 = rad * Math.sin(deg2rad((360 / vertexNum * i) - 90).to_f) + y)
    end
    ax.push(rad * Math.cos(deg2rad(-90 + theta).to_f) + x)
    ay.push(rad * Math.sin(deg2rad(-90 + theta).to_f) + y)
    convex(ax, ay, color, a)
end

# draw convex func
# [args] arrayX, arrayY, r, g, b, a
# [!Caution!] Enter counterclockwise
# [Reliance] isinside
def convex(ax, ay, color = [0, 0, 0], a = 0.0)
    setDrawArea(ax.min, ax.max, ay.min, ay.max)

    $j0.step($j1) do |j|
        $i0.step($i1) do |i|
            if isinside(i, j, ax, ay)
                if block_given?
                    yield(i, j)
                else
                    pset(i, j, color, a)
                end
            end
        end
    end
end

# draw func (drawPicture)
# [args] none
def draw
    #init
    init()

    # ここに処理を記述

    # write
    writeimage("img.ppm")
    return true
end

draw()

```