Andy Maleh
Posted on February 27, 2021
For the hardcore Computer Scientists out there who have strong interest in graphical algorithms, the Mandelbrot Set is a very well known Computer Science/Math algorithm that renders a fractal at various zoom levels, demonstrating the repetitive nature of fractals. During my bachelor of Computer Science at McGill University (Montreal, QC, Canada) over two decades ago, I remember spending very long and tiring nights at the computer lab to implement an Assembly language renderer of the Mandelbrot Fractal. Much has changed since then. We have multi-core processors today, let alone the wonderful Ruby programming language, so I wrote this with Glimmer DSL for SWT by taking advantage of the multi-threaded JRuby and saturating all CPU cores to finish calculating Mandelbrot points in less than 10 seconds on 4 CPU cores. The sample supports unlimited zooming, pre-calculated in the background with the window title bar notifying you once higher zooms are available. It also allows panning with scrollbars or mouse dragging.
The initial version's Glimmer GUI DSL (domain specific language) code was so simple, this was all of it:
body {
shell {
text 'Mandelbrot Fractal'
minimum_size @width, @height + 12
image @image
canvas {
image(@image, 0, 0)
}
}
}
Below is the multi-threaded model code and Glimmer GUI DSL code of the Mandelbrot Fractal initial version (without zooming or menus). The Glimmer DSL for SWT GUI simply paints an image as a form of image buffering (to avoid Mandelbrot pixel repaints on GUI repaints) and then puts it inside a canvas to display.
require 'glimmer-dsl-swt'
require 'complex'
require 'concurrent-ruby'
class Mandelbrot
attr_accessor :max_iterations
def initialize(max_iterations)
@max_iterations = max_iterations
end
def calculate_all(x_array, y_array)
thread_pool = Concurrent::FixedThreadPool.new(Concurrent.processor_count)
width = x_array.size
height = y_array.size
pixel_rows_array = Concurrent::Array.new(height)
height.times do |y|
pixel_rows_array[y] ||= Concurrent::Array.new(width)
width.times do |x|
thread_pool.post do
pixel_rows_array[y][x] = calculate(x_array[x], y_array[y]).last
end
end
end
thread_pool.shutdown
thread_pool.wait_for_termination
pixel_rows_array
end
# Mandelbrot point calculation implementation
# Courtesy of open-source code at:
# https://github.com/gotbadger/ruby-mandelbrot
def calculate(x,y)
base_case = [Complex(x,y), 0]
Array.new(max_iterations, base_case).inject(base_case) do |prev ,base|
z, itr = prev
c, _ = base
val = z*z + c
itr += 1 unless val.abs < 2
[val, itr]
end
end
end
class MandelbrotFractal
include Glimmer::UI::CustomShell
before_body {
@colors = [[0, 0, 0]] + 40.times.map { |i| [255 - i*5, 255 - i*5, 55 + i*5] }
@colors = @colors.map {|color_data| rgb(*color_data).swt_color}
mandelbrot = Mandelbrot.new(@colors.size - 1)
@y_array = (1.0).step(-1,-0.0030).to_a
@x_array = (-2.0).step(0.5,0.0030).to_a
@height = @y_array.size
@width = @x_array.size
@pixel_rows_array = mandelbrot.calculate_all(@x_array, @y_array)
@image = Image.new(display.swt_display, @width, @height)
image_gc = org.eclipse.swt.graphics.GC.new(@image)
@height.times { |y|
@width.times { |x|
new_foreground = @colors[@pixel_rows_array[y][x]]
image_gc.foreground = @current_foreground = new_foreground unless new_foreground == @current_foreground
image_gc.draw_point x, y
}
}
}
body {
shell {
text 'Mandelbrot Fractal'
minimum_size @width, @height + 12
image @image
canvas {
image(@image, 0, 0)
}
}
}
end
MandelbrotFractal.launch
Learn more at this blog post:
https://andymaleh.blogspot.com/2021/02/glimmer-dsl-for-swt-mandelbrot-fractal.html
Posted on February 27, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.