Ruby Fractal Library 1.1.0

Posted by Ryan Baxter Fri, 24 Oct 2008 18:49:00 GMT

Between spending time with the baby and working on a new project (more to come), I’ve found time to add a few features to the Ruby Fractal Library. An Algorithms module now contains lambda expressions implementing both the Escape Time and Normalized Iteration Count algorithms. Users can also create their own lambda expressions and assign them to the Fractal class’s algorithm property.

In the example below, I’ve show the difference between images rendered using the Escape Time and Normalized Iteration Count algorithms. As you can see, the Normalized Iteration Count algorithm generates images without the color banding associated with the Escape Time algorithm.

Escape Time Normalized Iteration Count

A Themes module now serves as a home for all of the library’s predefined color palettes. There are only two, but they’re easy to make. Since themes are also expressed as lambdas they too can be created by users and applied to the Fractal class. Below is my attempt at creating a snowflake using the Julia set and a user-defined theme.

snowflakes = Julia.new(Complex(-0.3007, 0.6601), 5, 100)
snowflakes.width = 350
snowflakes.height = 350
snowflakes.m = 2
snowflakes.set_color = PNG::Color::White
snowflakes.algorithm = Algorithms::NormalizedIterationCount
snowflakes.theme = lambda { |index|
  r, g, b = 0, 0, 0      
  if index >= 510
    r = 0
    g = 255 % index
    b = 255
  elsif index >= 255
    r = 0
    g = index % 255
    b = 255
  else    
    b = index % 255
  end      
  return r, g, b
}
snowflakes.draw('snowflakes.png')

Snowflakes was inspired by a colleague who wondered why I kept creating paisley. The Fire theme will do that. :)


When I get more time I’d like to implement some of the Escape Angle and Curvature Estimation algorithms as outlined by Garcia, Fernandez, Barrallo, and Martin, but for now I’d gladly accept any user-contributed algorithms or themes.

A fractal can now be instantiated with a single point rather than a range. This is the biggest breaking change over version 1.0.0. I believe that this makes the library easier to use and more similar to other fractal generating programs. The Fractal type also contains a where_is? method. This should help when trying to determine the complex coordinate of an x, y value pair.

In testing the library, I attempted to generate a few of the fractals found in the Mandelbrot set Wikipedia entry. The images found at Wikipedia were rendered using Ultra Fractal 3 and are beautiful. Knowing Ruby, I didn’t expect to generate images with the same quality, but I was pleasantly surprised. Here is “Satellite” followed by the Misiurewicz point. Both were rendered with the Ruby Fractal Library.

Satellite Misiurewicz point


Satellite can be found where c = -0.743643135, 0.131825963i at around 200k magnification. I had to set max_iterations = 1500 to get this level of detail. It still took a few minutes to render using the latest YARV interpreter on an Intel Core 2 Duo 2.6Ghz. Overall I’ve noticed that YARV finishes rendering the Mandelbrot set in approximately half the time of the old Matz interpreter. Not a bad gain. It’ll never be as quick as C, but I still look forward to Ruby 2.0!

The Ruby Fractal Library can be found under the “Projects” section of this website. Feel free to send me any feedback. I’d love to see some new color themes or algorithms.

Installing Ruby 1.8 and 1.9 on Ubuntu from Source

Posted by Ryan Baxter Sun, 14 Sep 2008 20:30:00 GMT

I’ve begun testing my fractal library with the latest source code from the Ruby 1.9 trunk. Since 1.9 is a development release and I still need 1.8 for my Rails applications, I’ve checked out both versions and configured them to run side-by-side on Ubuntu, Hardy Heron.

Before you begin, make sure you have the autoconf, build-essential, and subversion packages installed.

It may also be a good idea to include the Ruby1.8 and Ruby1.9 build dependencies from Ubuntu’s package repository. This could prevent some headaches later on.

$ sudo apt-get build-dep ruby1.8 ruby1.9

Create a directory for the Ruby source code.

$ mkdir /home/ryan/source
$ cd /home/ryan/source

Check out the code from the 1.8 branch. Since this branch includes patches, you can always update your source and recompile when new patches are released.

$ svn co http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8 ruby1.8

Next, create a configuration, configure, and compile.

$ cd ruby1.8
$ autoconf
$ ./configure --prefix=/opt/ruby1.8 --program-suffix=1.8
$ sudo make
$ sudo make install

Finally, link your new binaries to the /usr/local/bin directory.

$ sudo ln -s /opt/ruby1.8/bin/* /usr/local/bin

Typing ruby1.8 -v in a new console should yield something similar to the following:

$ ruby1.8 -v
ruby 1.8.7 (2008-09-15 revision 19348) [i686-linux]

One of the best parts of Ruby is Rubygems! Download and install it.

$ wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
$ tar -xvzf rubygems-1.2.0.tgz
$ cd rubygems-1.2.0
$ sudo ruby1.8 setup.rb
$ sudo ln -s /opt/ruby1.8/bin/gem1.8 /usr/local/bin

The current source for Ruby 1.9 requires version 1.8 to compile, but if you’ve followed my directions up to this point you should be ready to download and compile the latest Ruby 1.9 source code.

Go back to your source directory and check out the latest code from the 1.9 trunk.

$ cd /home/ryan/source
$ svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby1.9
$ cd ruby1.9

This time use the –with-baseruby switch when configuring. Set this option to the new Ruby 1.8 binary and then compile.

$ autoconf
$ ./configure --with-baseruby=/usr/local/bin/ruby1.8 --prefix=/opt/ruby1.9 --program-suffix=1.9
$ sudo make
$ sudo make install

Finish by linking your new Ruby 1.9 binaries to /usr/local/bin.

$ sudo ln -s /opt/ruby1.9/bin/* /usr/local/bin

Typing ruby1.9 -v in your console should yield something similar to the following:

$ ruby1.9 -v
ruby 1.9.0 (2008-09-15 revision 19351) [i686-linux]

Ruby 1.9 includes Rubygems! We’re done!

Since the binaries we’ve created have been suffixed with either 1.8 or 1.9, you must remember to execute them with their proper name ie (gem1.8, gem1.9, ruby1.8, ri1.9).

I’ve written these instructions from memory so if you have any problems, please post them and I’ll try to help out as best I can. Please be warned, however, that not all code written for Ruby 1.8 will work in 1.9.

Ruby Fractal Library

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

*Update: Version 1.1.0 of the Ruby Fractal Library has been released.

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.

Older posts: 1 2 3 4 5 6 ... 8