LoginSignup
2
1

Crystalでマンデルブロ集合を描画しPNGに出力する

Last updated at Posted at 2023-12-11

本日は kojix2 がネタ切れのために逃亡したため、代わりにChatGPTが記事を書いています。

以下のファイルは絶対に見ないように。kojix2より

mandelbrot_custom_color.png

はじめに

こんにちは、ChatGPTです。今日は特別にkojix2さんの代わりに、Crystal言語を使ったプログラミングの楽しさをお伝えします!数学とアートの融合に挑戦してみませんか?心配は無用、一緒にマンデルブロ集合の美しい世界を探索しましょう。この冒険では、Crystal言語を使ってマンデルブロ集合を描画し、PNGファイルとして記録します。

プロジェクトの作成

まずは新しいCrystalプロジェクト mandel を作成します。

crystal init app mandel
cd mandel

次に、shards.yml ファイルを編集して、必要な依存関係を追加します。

vi shards.yml
dependencies:
  vips:
    github: naqvis/crystal-vips

依存関係をインストールします。

shards install

コードの編集

src/mandel.cr を編集します。

require "complex"
require "option_parser"
require "vips"

# Default parameters
width = 800
height = 800
x_min = -2.0
x_max = 1.0
y_min = -1.5
y_max = 1.5
max_iter = 100
output_filename = "mandelbrot_custom_color.pnm"

# Option parser
OptionParser.parse do |parser|
  parser.banner = "Usage: mandelbrot [arguments]"
  parser.on("-w WIDTH", "--width=WIDTH", "Width of the image (default: #{width})") { |w| width = w.to_i }
  parser.on("-h HEIGHT", "--height=HEIGHT", "Height of the image (default: #{height})") { |h| height = h.to_i }
  parser.on("--xmin=VALUE", "Minimum x-coordinate value (default: #{x_min})") { |xmin| x_min = xmin.to_f }
  parser.on("--xmax=VALUE", "Maximum x-coordinate value (default: #{x_max})") { |xmax| x_max = xmax.to_f }
  parser.on("--ymin=VALUE", "Minimum y-coordinate value (default: #{y_min})") { |ymin| y_min = ymin.to_f }
  parser.on("--ymax=VALUE", "Maximum y-coordinate value (default: #{y_max})") { |ymax| y_max = ymax.to_f }
  parser.on("-i ITERATIONS", "--iterations=ITERATIONS", "Maximum iterations (default: #{max_iter})") { |iter| max_iter = iter.to_i }
  parser.on("-o FILENAME", "--output=FILENAME", "Output file name (default: '#{output_filename}')") { |filename| output_filename = filename }
  parser.on("--help", "Show this help") do
    puts parser
    exit
  end
  parser.invalid_option do |flag|
    STDERR.puts "ERROR: #{flag} is not a valid option."
    STDERR.puts parser
    exit(1)
  end
end

def mandelbrot_set(width, height, x_min, x_max, y_min, y_max, max_iter)
  dx = (x_max - x_min).to_f64 / width
  dy = (y_max - y_min).to_f64 / height

  Array.new(height) do |y|
    Array.new(width) do |x|
      c = Complex.new(x_min + x * dx, y_min + y * dy)
      z = Complex.new(0, 0)
      iter = 0

      while z.abs <= 2 && iter < max_iter
        z = z * z + c
        iter += 1
      end

      iter
    end
  end
end

def write_to_custom_color_pnm(image, max_iter)
  String.build do |pnm_data|
    # PNMヘッダの生成
    pnm_data << "P3\n#{image[0].size} #{image.size}\n255\n"

    # PNMデータの生成
    image.each do |row|
      row.each do |value|
        red = (255 * Math.sin(0.16 * value)).abs.to_i % 256
        green = (255 * Math.sin(0.11 * value + 2)).abs.to_i % 256
        blue = (255 * Math.sin(0.06 * value + 4)).abs.to_i % 256
        pnm_data << "#{red} #{green} #{blue} "
      end
      pnm_data << "\n"
    end
  end
end

def save_as_png(pnm_data, png_filename)
  # Vipsを使用してPNMデータをPNGとして保存
  image = Vips::Image.new_from_buffer(pnm_data, "")
  image.write_to_file(png_filename)
end

# マンデルブロ集合の生成
mandelbrot_image = mandelbrot_set(width, height, x_min, x_max, y_min, y_max, max_iter)

# PNMデータの生成
pnm_data = write_to_custom_color_pnm(mandelbrot_image, max_iter)

# PNGファイルとして保存
png_filename = output_filename.sub(/\.[^.]+\z/, ".png") # PNGファイル名へ変更
save_as_png(pnm_data, png_filename)

ビルドと実行

プロジェクトをビルドします。

shards build

ビルドが完了したら、実行ファイルを起動します。

bin/mandel

これにより、マンデルブロ集合の画像がPNGファイルとして出力されます。

終わりに

Crystal言語を使うと、数学的な美しさを持つ図形を生成し、画像処理を行うことも簡単にできます。このチュートリアルでは、マンデルブロ集合の生成とPNGファイルへの出力を通じて、Crystalの基本的な使用方法と外部ライブラリの組み込み方を学びました。さあ、この経験を活かして、さまざまなプロジェクトに挑戦してみましょう。

この記事は以上です。


なんかテンション高くないっすか? (kojix2)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1