Ruby Fractal Library

Posted by Ryan Baxter Thu, 03 Jul 2008 21:26:00 GMT

Last October, I wrote a small fractal rendering program in Ruby using the Shoes windowing toolkit written by why the lucky stiff. It’s sole purpose was to test Shoes. The code was painfully slow at rendering the Mandelbrot set, but it did, however, begin a small obsession of mine with fractals.

Since I couldn’t find a fractal library for Ruby, I decided to write one. Over the last two weeks I’ve written some code to generate both the Mandelbrot and Julia set fractals using the escape time algorithm. The code is still slow, but within a couple weeks I hope to replace the slow portions with inline C.

There may still be some bugs and I haven’t added any error handling, but here it is. An “almost” pure Ruby fractal library. Once this is cleaned up I’ll repost the code. I suppose a gem could be possible as well. Happy 4th!

fractals.rb

# RB

require 'rubygems'
require 'complex'
require 'png'

module Fractals
  module Fractal
    attr_accessor :begin_range, :end_range

    def initialize(begin_range, end_range)
      @begin_range, @end_range = begin_range, end_range
    end

    def draw(height=250, width=250, m=1.0, save_as='fractal.png')
        canvas = PNG::Canvas.new(height, width)

        # Find the complex coordinate for each pixel.
        0.upto(height - 1) { |y|
          i = (y * (@end_range.image - @begin_range.image) / height +
          @begin_range.image) * m
          0.upto(width - 1) { |x|
            r = (x * (@end_range.real - @begin_range.real) / width +
            @begin_range.real) * m
            if self.in_set?(Complex(r, i)) then
              canvas[x, y] = PNG::Color::Black
            else
              canvas[x, y] = fetch_color(self.last_iteration, self.max_iterations)
            end                    
          }
        }

        png = PNG.new(canvas)
        png.save(save_as)
    end  

    private
    def fetch_color(last_iteration, max_iterations)  
      divisor = 765*last_iteration/max_iterations
      case divisor
        when 0..254 then return PNG::Color.new(divisor%255, 0, 0, 255)
        when 255..509 then return PNG::Color.new(255, divisor%255, 0, 255)
        when 510..765 then return PNG::Color.new(255, 255, divisor%255, 255)
      end       
    end
  end

  class Julia
    include Fractal
    attr_accessor :seed, :bailout, :max_iterations
    attr_reader :last_iteration

    def initialize(seed=Complex(0.36, 0.1), bailout=2, max_iterations=100,
    begin_range=Complex(-2.25, -1.5), end_range=Complex(0.75, 1.5))
      super(begin_range, end_range)
      @seed, @bailout, @max_iterations = seed, bailout, max_iterations
    end

    def in_set?(z)
      @max_iterations.times { |i|
        z = z**2 + @seed
        if z > @bailout then
          @last_iteration = i
          return false
        end      
      }
      return true
    end
  end

  class Mandelbrot
    include Fractal  
    attr_accessor :bailout, :max_iterations
    attr_reader :last_iteration

    def initialize(bailout=5, max_iterations=100, begin_range=Complex(-2.25,
    -1.5), end_range=Complex(0.75, 1.5))
      super(begin_range, end_range)
      @bailout, @max_iterations = bailout, max_iterations
    end

    def in_set?(c)
      z = 0
      @max_iterations.times { |i|
        z = z**2 + c
        if z > @bailout then
          @last_iteration = i
          return false
        end            
      }
      return true
    end
  end
end

Using this library is as simple as the following:

require 'fractals'

mandelbrot = Fractals::Mandelbrot.new
mandelbrot.draw

Any suggestions/bug fixes can be posted here. Thanks.

Trackbacks

Use the following link to trackback from your own site:
http://crunchlife.com/articles/trackback/70

Comments

Leave a response

Avatar
daniel[put-a-dot-here]pruessner@ieee.org Comment_bubble 2 months later:
Ryan, Thanks for posting this! It's a great starting point for some fun Ruby fractal projects. On Ruby1.9, I did have to change two lines to read: 'if z.abs @bailout ...' Dunno if you/others encountered that. Daniel
Avatar
Ryan Baxter Comment_bubble 2 months later:
Thanks Daniel! I'll repost a corrected version soon. Please let me know if you have any other suggestions.