Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Showing posts with label mandelbrot. Show all posts
Showing posts with label mandelbrot. Show all posts

Thursday, 25 December 2014

Towards a zoom-able mandelbrot ruby-processing

Working toward a zoom-able mandelbrot (albeit somewhat speed limited in ruby-processing)
Parameter = Struct.new(:cx, :cy, :ch, :cw) # required for future

RATIO = 1.336
TMAX = 5_000

attr_reader :current, :default # required for future work

def setup
  size(534, 400, P2D)
  @default = Parameter.new(-0.53 - (1.4 * RATIO), -1.4, 2.8, 2.8 * RATIO)
  @current = default
  load_pixels
  no_fill
  stroke(255)
  create_mandelbrot(width, height)
  no_loop
end

def create_mandelbrot(w, h)
  y_zero = ->(y, param) { y * param.ch / height + param.cy }
  x_zero = ->(x, param) { x * param.cw / width + param.cx }
  h.times do |j|
    y0 = y_zero.call(j, current)
    w.times do |k|
      x0 = x_zero.call(k, current)
      x, y = 0, 0
      xsqr, ysqr = 0.0, 0.0
      t = 0
      while t < TMAX && (xsqr + ysqr < 4.0)
        y = x * y
        y += y + y0
        x = xsqr - ysqr + x0
        xsqr = x * x
        ysqr = y * y
        t += 1
      end
      if t < TMAX
        smth = t - Math.log(xsqr + ysqr)
        pixels[k + j * width] = Java::JavaAwt::Color.HSBtoRGB(smth / 500, 0.8, 0.9)
      else
        pixels[k + j * width] = 0
      end
    end
  end
  update_pixels
end

Thursday, 14 August 2014

Experimental map1d method JRubyArt (ruby-processing development)

The recently updated JRubyArt has now removed the processing convenience functions that were deprecated in ruby-processing-2.5.1. Other functions such as map are retained, but I wondered would a map1d range method be a more ruby like alternative:-
# @param [float] val
# @param [range(start1 .. end1)] r_in
# @param [range(start2 .. end2)] r_out
# @return [float] mapped value
#
def map1d(val, r_in, r_out)
  r_out.first + (r_out.last - r_out.first) * ((val - r_in.first).to_f / (r_in.last - r_in.first))
end

# usage map1d(5, (0 .. 10), (10 .. 110))           exclude end is false

puts map1d(5, (0 .. 10), (10 .. 110))

# output 60.0

Here is the method in action
# Mandelbrot Set example
# by Jordan Scales (http://jordanscales.com)
# 27 Dec 2012
# Modified to use map1d (instead of map), and somewhat 
# optimized (update_pixels instead of set, and hypot for abs)

# default size 900x600
# no need to loop
def setup
  size 900, 600
  load_pixels
  no_loop
end

# main drawing method
def draw
  (0 ... 900).each do |x|
    (0 ... 600).each do |y|
      c = Complex.new(map1d(x, (0 .. 900), (-3 .. 1.5)), map1d(y, (0 .. 600), (-1.5 .. 1.5)))
      # mandel will return 0 to 20 (20 is strong)
      #   map this to 0, 255 (and flip it)
      pixels[x + y * 900] = color(255 - map1d(mandel(c, 20), (0 .. 20), (0 .. 255)).to_i)
    end
  end
  update_pixels
end

# calculates the "accuracy" of a given point in the mandelbrot set
#    : how many iterations the number survives without becoming chaotic
def mandel(z, max = 10)
  score = 0
  c = z.clone
  while score < max 
    # z = z^2 + c
    z.square
    z.add c
    break if z.abs > 2

    score += 1
  end

  score
end


# rolled my own Complex class
#   stripped of all functionality, except for what I needed (abs, square, add, to_s)
#   
#   Using this class, runs in ~12.5s on my MacBook Air
#     compared to ~22s using ruby's Complex struct
class Complex

  attr_accessor :real, :imag

  def initialize(real, imag)
    @real = real
    @imag = imag
  end

  # square a complex number - overwriting it
  def square
    r = real * real - imag * imag
    i = 2 * real * imag

    @real = r
    @imag = i
  end

  # add a given complex number
  def add(c)
    @real += c.real
    @imag += c.imag
  end

  # compute the magnitude
  def abs
    hypot(real, imag)
  end

  def to_s
    "#{real} + #{imag}i"
  end

end

PS: I have not tested whether Jordans findings re: ruby complex implementation still hold true (jruby truffle claims wicked fast mandelbrot calculations). Benchmarking is tricky with jvm warmup up time, I have replaced sqrt(real * real + imag * imag) (abs method) with hypot(real, imag), which should be more efficient...

Followers

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2