Glimmer DSL for SWT Mandelbrot Fractal
Tags: ruby, gui, graphics, desktop
Published: 2021-02-27 06:23:25

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.

![Glimmer DSL for SWT Mandelbrot Fractal](https://lh3.googleusercontent.com/-1aOMtrXxN1w/YCvdpNoHeAI/AAAAAAAABmY/R6kATvUyz7QSYrNugPsV4bkzJLQB03-FwCLcBGAsYHQ/glimmer-mandelbrot-zoom1.png)

The initial version's Glimmer GUI DSL (domain specific language) code was so simple, this was all of it:

```ruby
  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.

```ruby
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