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

Saturday, 3 August 2013

Using MDArray in ruby-processing

Well quite excited with this jruby gets its own flavour of numpy, namely MDArray. I had to give it a go here is a simple example:-
#  Demonstrates the syntax for creating a two-dimensional (2D) array,
#  fromfunction (actually a block) using MDArray (for jruby).
#  Values in a 2D array are accessed through two index values.  
#  2D arrays are useful for storing images. In this example, each dot 
#  is colored in relation to its distance from the center of the image.

require 'mdarray'

WIDTH=640
HEIGHT=360
SKIP=10

def setup
  size WIDTH, HEIGHT
  background 0
  stroke_weight 2
  max_distance = ( (WIDTH / 2 - WIDTH)**2  + (HEIGHT / 2 - HEIGHT)**2 )**0.5
  distances = MDArray.fromfunction("float", [WIDTH, HEIGHT]) do |x, y|
    255 * ( (WIDTH / 2 - x)**2  + (HEIGHT / 2 - y)**2 )**0.5 / max_distance
  end
  (SKIP ... WIDTH).step(SKIP) do |x|
      (SKIP ... HEIGHT).step(SKIP) do |y|
        stroke distances[x, y]
        point x, y
      end
  end
end


According to the limited documentation it might be more efficient to use the accessor to "get" data rather than using bare array access see below. For actual image manipulation in ruby-processing we might be in a bind, we require external jruby to access the gem, but that appears not to work with PImage sketches?
distances.get([x, y])

Monday, 22 July 2013

Clobbering archived files in a github repository

Now largely thanks to moumar, we are good guys on ruby-processing because we no longer use unnecessary binaries in the ruby-processing repo (they are downloaded directly during autobuild process) but whoa! the download was 70Mb. So after some research I found unused binaries "jruby-complete.jar" "core.jar" etc are archived in the .git/object directory. So what to do, well fortunately there is this BFG tool that makes it easy to clobber the offending commits, but the references all get re-written so any forks should made anew. To make sure I have created a new repo (not a fork) from a clobbered mirrored clone of ruby-processing. The repo is now ca 10.75Mb vs ca 70Mb.
A couple of useful commands that came in handy in checking and setting remote.

git remote -v

git remote set-url origin https://github.com/monkstone/remote.git

Friday, 19 July 2013

Fibonacci Sphere in Ruby Processing

# fibonacci_sphere.rb
# After a vanilla processing sketch by Jim Bumgardner
# http://www.openprocessing.org/sketch/41142
#
# Controls:
# 1. drag mouse to rotate sphere (uses builtin arcball library)
# 2. click mouse to toggle add box to sphere surface
# 3. press x, y, or z to constrain arcball rotation to that axis
#

load_library :arcball
import "arcball"

X = 0
Y = 1
Z = 2

PHI = (sqrt(5)+1) / 2 - 1   # golden ratio
GA = PHI * TWO_PI           # golden angle

KMAX_POINTS = 100000

attr_reader :pts, :rotation_x, :rotation_y, :nbr_points, :radius, :add_points
attr_reader :my_ball # for arcball rotation

def setup
  size(1024, 768, P3D)
  @my_ball = ArcBall.new(width/2.0, height/2.0, min(width - 20, height - 20) * 0.5)
  @rotation_x = 0
  @rotation_y = 0
  @nbr_points = 2000
  @radius = 0.8 * height / 2
  @add_points = true
  @pts = Array.new(KMAX_POINTS)
  init_sphere(nbr_points)
  background(0)
end

def draw
  if add_points
    @nbr_points += 1
    @nbr_points = min(nbr_points, KMAX_POINTS)
    init_sphere(nbr_points)
  end

  background 0
  lights
  ambient(200, 10, 10)
  ambient_light(150, 150, 150)
  translate(width/2.0, height/2.0, 0)
  update  # for arcball rotation
  render_globe
end

# arcball functionality ##################
##########################################

def update
  theta, x, y, z = my_ball.update
  rotate(theta, x, y, z)
end

def mouse_pressed
  my_ball.mouse_pressed(mouse_x, mouse_y)
end

def mouse_dragged
  my_ball.mouse_dragged(mouse_x, mouse_y)
end

def key_pressed
  case(key)
  when 'x'
    my_ball.select_axis(X)
  when 'y'
    my_ball.select_axis(Y)
  when 'z'
    my_ball.select_axis(Z)
  end
end

def key_released
  my_ball.select_axis(-1)
end
###########################################
# For Fibonacci Sphere
##################################

def render_globe
  push_matrix
  (0 .. min(nbr_points, pts.length)).each do |i|
    lat = pts[i].lat
    lon = pts[i].lon
    push_matrix
    rotate_y(lon)
    rotate_z(-lat)
    fill(200, 10, 10)
    translate(radius, 0, 0)
    box(4, 7, 7)
    pop_matrix
  end
  pop_matrix
end

def mouse_clicked
  @add_points = !add_points
end

SpherePoint = Struct.new(:lat, :lon) do
end

def init_sphere(num)
  (0 .. num).each do |i|
    lon = GA * i
    lon /= TWO_PI
    lon -= lon.floor
    lon *= TWO_PI
    if (lon > PI)
      lon -= TWO_PI
    end
    # Convert dome height (which is proportional to surface area) to latitude
    # lat = asin(-1 + 2 * i / num.to_f)    
    pts[i] = SpherePoint.new(asin(-1 + 2 * i / num.to_f), lon)
  end
end

Here's a very equivalent sketch I published at openprocessing

Thursday, 18 July 2013

A simple ruby-processing paint application

#
# Painting Application in Ruby-Processing
# 
# Click and drag the mouse to paint. 
#

load_library :control_panel

attr_reader :panel, :red, :alpha, :green, :erase, :brush, :blue, :sz

def setup
  size(640, 360)
  color_mode RGB, 1.0
  background(0.8)
  control_panel do |c|
    c.slider    :red, 0.0..1.0, 1.0
    c.slider    :green, 0.0..1.0, 0.0
    c.slider    :blue, 0.0..1.0, 0.0
    c.slider    :alpha,  0.0..1.0, 0.9
    c.menu(:brush, ['big', 'medium', 'fine', 'pencil'], 'medium') {|m| load_menu_item(m) }
    c.checkbox  :erase
    c.button    :print_color
    c.button    :clear!
    @panel = c
  end
end

def draw
  no_stroke
  erase ? fill(0.8) : fill(red, green, blue, alpha)
  panel.set_visible true if self.visible?
  if mouse_pressed?
    ellipse(mouse_x, mouse_y, sz, sz)
  end
end

def clear!
  background(0.8)
end

def load_menu_item m
  chosen = brush[m]
  @sz = {'big' => 20, 'medium' => 10, 'fine' => 5, 'pencil' => 2}[chosen]
  return chosen
end

def print_color
  puts "red=#{(red*255).to_i}, blue=#{(blue*255).to_i}, green=#{(green*255).to_i}, alpha=#{(alpha*255).to_i}"
end

Monday, 15 July 2013

Using Threads in Ruby-Processing

It has taken a bit of time but finally I got round to rubifying vanilla processing threads example. Avoids nasty reflection nonsense use by processing convenience method thread(some_method). Which I have just overridden, which now like fork and Thread.new in ruby, takes a block (my method is actually just a convenience wrapper for Thread.new, so it is not really useful, apart from providing a method that people coming from vanilla processing might expect.
# 
# Thread function example
# by Martin Prout (after a Dan Schiffman vanilla processing version).  
# 
# This example demonstrates how to use Thread.new to spawn
# a process that happens outside of the main animation thread.
#
# When Thread.new is called, the draw() loop will continue while
# the code inside the block passed to the thread will operate
# in the background.
#
# For more about threads, see: http://wiki.processing.org/w/Threading
#

# This sketch will load data from all of these URLs in a separate thread
URLS = [
"http://processing.org",
"http://www.processing.org/exhibition/",
"http://www.processing.org/reference/",
"http://www.processing.org/reference/libraries",
"http://www.processing.org/reference/tools",
"http://www.processing.org/reference/environment",
"http://www.processing.org/learning/",
"http://www.processing.org/learning/basics/",
"http://www.processing.org/learning/topics/",
"http://www.processing.org/learning/gettingstarted/",
"http://www.processing.org/download/",
"http://www.processing.org/shop/",
"http://www.processing.org/about/"
]


attr_reader :finished, :percent

def setup
  size(640, 360)
  # Spawn the thread!
  # This will keep track of whether the thread is finished
  load_data
end

def draw
  background(0)

  # If we're not finished draw a "loading bar"
  # This is so that we can see the progress of the thread
  # This would not be necessary in a sketch where you wanted to load data in the background
  # and hide this from the user, allowing the draw() loop to simply continue
  if (!finished)
    stroke(255)
    no_fill()
    rect(width/2-150, height/2, 300, 10)
    fill(255)
    # The size of the rectangle is mapped to the percentage completed
    w = map(percent, 0, 1, 0, 300)
    rect(width/2-150, height/2, w, 10)
    text_size(16)
    text_align(CENTER)
    fill(255)
    text("Loading", width/2, height/2+30)
  else
    # The thread is complete!
    text_align(CENTER)
    text_size(24)
    fill(255)
    text("Finished loading. Click the mouse to load again.", width/2, height/2)
  end
end

def load_data
  Thread.new {
    # The thread is not completed
    @finished = false
    @percent = 0
    # Reset the data to empty
    @all_data = ""
    URLS.each_with_index do |url, i|
      lines = load_strings(url)
      all_txt = lines.join(' ')
      words = all_txt.scan(/\w+/)
      words.each do |word|
        word.strip!
        word.downcase!
      end
      words.sort!
      @all_data << words.join(' ')
      @percent = i.to_f / URLS.length
    end
    @finished = true
  }
end

def mouse_pressed
  load_data
end

Saturday, 13 July 2013

Yet Another 10 PRINT sketch in ruby-processing

Added some tweaks for smoother operation ie key_pressed/key_released to sequence action. See at open processing here.
# 
# Inspired by:
# 10 PRINT CHR$(205.5+RND(1)) : GOTO 10
# 
# Sketch by:
# Jacob Joaquin
# jacobjoaquin@gmail.com
# twitter @jacobjoaquin
# 

ODDS = 0.618

attr_reader :num_tiles_x, :num_tiles_y, :tile_size, :c1, :c2

def setup
  size 800, 800
  @tile_size = 50
  @num_tiles_x = width / tile_size
  @num_tiles_y = height / tile_size
  @c2 = color(235, 70, 47)
  @c1 = color(255)
  background c2
  fill c1
  no_stroke
  no_loop
end

def draw
  do_maze
end

def key_pressed
  case key
  when 'q'
    resize_tile 2
  when 'w'
    resize_tile 0.5
  when 's'
    save "maze_sketch.png"
  end
end

def key_released
  case key
  when 'q', 'w'
    background c2
    redraw
  end
end

def resize_tile v
  @tile_size *= v
  @tile_size *= v
  @num_tiles_x = width / tile_size
  @num_tiles_y = height / tile_size
end

def do_maze
  (-1 .. num_tiles_x).each do |i|
    (-1 .. num_tiles_y).each do |j|
      x1, y1 = i * tile_size, j * tile_size
      x2, y2 = x1, y1 + tile_size
      d = height / (tile_size / 2)
      n = y1 / d
      x = y2 / d

      if (rand > ODDS)
        x1 += tile_size
        quad(x1, y1 - n, x1 + n, y1, x2, y2 + x, x2 - x, y2)
      else
        x2 += tile_size
        quad(x1 - n, y1, x1, y1 - n, x2 + x, y2, x2, y2 + x)
      end
    end
  end
end

Another 10 PRINT sketch translated to ruby-processing

Catch the live version here.
#####################
#                                      #
#    10 PRINT - Stacked 3D Version     #
#                                      #
#####################

# original by bit.craft

W = 400   # canvas size
N = 10    # number of grid cells
D = W/N   # diameter of a grid cell
DPT = 0.5 # relative cell depth

def setup
  size W, W, P3D
  rect_mode CENTER
  background 255
  stroke_weight 3
end

def draw
  # get coordinates
  xy = frame_count % (N*N)
  # shift image in z-direction
  if (xy == 0)
    img = get
    background 255
    push_matrix
    translate 0, 0, -D * DPT
    fill 204
    no_stroke
    image img, 0, 0
    pop_matrix
  end

  # scale and rotate the square
  scale D
  translate (xy % N) + 0.5, (xy/N).to_i + 0.5, - DPT * 0.5
  rotate_z QUARTER_PI - HALF_PI * rand(2)
  rotate_x HALF_PI

  # draw the square
  stroke 0
  fill 255
  rect 0, 0, sqrt(2), DPT
end

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