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

Monday 26 August 2013

Simple pixellator using a cfdg end point

In hommage to guigui's original, the output cfdg file is called haddock.cfdg (after capt haddock "tintin"), the sketch data gets shoved into data.cfdg, that is called from haddock.cfdg by the cfdg program (requires version 3.0+). To adjust to different axis conventions I could just have made y negative. But because evolution of an non deterministic design (this isn't one) is from centre out, I adjusted x and y accordingly. Tune-able value is skip 3..10 is sensible choice, you could also add a posterize filter if needed. Nice to have would be a rectangle mask to define area to pixel-ate. Where an actual image is uses as basis you might be better doing img.load_pixels and accessing the pixel array thus img.pixels[x +y * width]. However the following sketch could be readily adapted to use processing generated images.
load_library :file_chooser
attr_reader :img, :data, :skip

###########
# Example file chooser (in this case as an image file chooser).
# We delay setting size of sketch until we know image size, probably
# would not work vanilla processing. Note we can wrap much code in the
# file_chooser block, no need for reflection. As with selectInput vanilla
# processing.
###########

def setup
  color_mode(HSB, 1.0);
  file_chooser do |fc|
    fc.look_feel "Gtk+"                            # optional, default is "Metal"
    fc.set_filter "Image Files",  [".png", ".jpg"] # easily customizable chooser
    @img = load_image(fc.display)                  # fc.display returns a path String
    size(img.width, img.height)
  end
  @skip = 5
  @data = []
end

def draw
  background img                                  # img must be same size as sketch
end

def write_data name, data
  open("data.cfdg", "w") do |pw|
    pw.puts "shape #{name}{\n"
    data.each do |row|
      pw.puts "  #{row[0]}[x #{row[1]} y #{row[2]} s #{row[3]} hue #{row[4]} sat #{row[5]} brightness #{row[6]}]\n"
    end
    pw.puts "}\n"
  end
end

def write_start start, data
  open("#{start}.cfdg", "w") do |pw|
  pw.puts "CF::Background = [b -1]"
  pw.puts "startshape #{start}\n"
  pw.puts "shape dot{CIRCLE[]}\n"
  pw.puts "import data.cfdg\n"
end
write_data start, data
end

def mouse_clicked
  export = Thread.new do
    load_pixels
    shp = "dot"
    (skip ... img.width).step(skip) do |x|
      (skip ... img.height).step(skip) do |y|
        pix = pixels[x +y * width]
        sat = saturation(pix)
        hue = hue(pix)
        sz = brightness(pix) * skip
        data << [shp, -width/2 + x, height/2 - y, sz.round(2), (hue * 360).round, sat.round(4), 1] if sz > 0.03
      end
    end
    write_start "haddock", data
  end  
  export.join
  puts "done"
end

Sunday 25 August 2013

The Gtk+ look and feel using openjdk

I just did some further experiments with my ruby-processing file chooser, and now I think the "Gtk+" look and feel might be the way to go on linux:-
However it is not that you should call the "Gtk+" look and feel, you should probably call "Metal", but specify system wide that the system and default look and feel is GTK (on archlinux you can do this in /etc/jre.sh in others you might use a flag or otherwise specify an environmental variable, be sure to set GTK fonts too).

Friday 23 August 2013

Image Viewer Ruby Processing

Here is a little sketch, exploring the possibility of using my file_chooser to create a neat little image viewer. I does not make too much sense on its own, but we can take a look at images in ruby-processing, and then possibly do something with them along the lines of exporting to a "pixelated" sketch to cfdg for example.
load_library :file_chooser
attr_reader :img

###########
# Example usage of file_chooser (in this case image file chooser) 
# not sure this is exactly possible with vanilla processing. Here we
# defer setting the size of the sketch, until we know the size of image.
###########

def setup
  file_chooser do |fc|
    fc.look_feel "Nimbus"
    fc.set_filter "Image Files",  [".png", ".jpg"]
    my_file = fc.display
    @img = load_image(my_file)
    size img.width, img.height
  end
end

def draw
  background img
end

File Chooser Library For ruby-processing

Well here it is in action my ruby-processing file_chooser library, that like control panel takes a block the fc.display returns a a string that you can use in load_image etc to load a file, you can do all this in a block if you like (see below). Is a replacement for vanilla processing selectInput function that relies on reflection (and the chooser cannot be so easily customised). PS the my_file temporary variable is not required. The chooser starts in the users home folder on unix (mac/linux) or the working directory on windoes.
load_library :file_chooser
attr_reader :img

###########
# Example file chooser (in this case image file chooser)
# the image loading and resizing is encapsulated in the block,
# borrows heavily off control_panel. 
###########

def setup
  size 600, 600
  file_chooser do |fc|
    fc.look_feel "Nimbus"
    fc.set_filter "Image Files",  [".png", ".jpg"]
    my_file = fc.display
    @img = load_image(my_file)
    img.resize(width, height)
  end
end

def draw
  image img, 0, 0
end

The Chooser (a customised JFileChooser)
The Sketch

Saturday 17 August 2013

File Chooser in JRuby

Having convinced myself that the vanilla processing selectInput is a piece of crap (yet more reflection nonsense) I though I had better come up with an alternative for ruby processing, here is such an example (yielded by my good friend google and a bit of imagination, works for me):-
require 'rbconfig'
require 'pathname'

System = Java::JavaLang::System
JFile = Java::JavaIo::File
JXChooser = Java::javax::swing::JFileChooser

class ImageFilter < Java::javax::swing::filechooser::FileFilter
  def accept fobj
    return [".png",".jpg"].include? File.extname(fobj.to_s).downcase
    return fobj.isDirectory
  end

  def getDescription
    "Image Files"
  end
end


# Detect OS
OS = case RbConfig::CONFIG['host_os']
  when /darwin/      then :mac
  when /mswin|mingw/ then :windows
else
  :unix
end

# Asks the user to choose an editor.
# Returns either a Pathname object or nil (if canceled)
def pickImage
  # Create a FileChooser
  fc = JXChooser.new("Image Chooser")
  fc.set_dialog_title("Please select an image")
  fc.setFileFilter ImageFilter.new

  if :mac == OS
    fc.setCurrentDirectory(JFile.new(System.getProperty("user.home") << "/Pictures"))
  elsif :windows == OS
    fc.setCurrentDirectory(JFile.new(System.getProperty("user.dir")))
  else
    fc.setCurrentDirectory(JFile.new(System.getProperty("user.home")))
  end

  success = fc.show_open_dialog(nil)
  if success == JXChooser::APPROVE_OPTION
    return Pathname.new(fc.get_selected_file.get_absolute_path)
  else
    nil
  end
end

puts "The user picked: #{pickImage}"

# EOF
This would be the wrapper for ruby-processing?
def select_input(*args, &block)
  if block_given?
    # code using FileChooser output?
  else
    raise ArgumentError, "select_input must be called with a block" , caller
  end
end

Thursday 15 August 2013

Ruby-processing-2.1.3 is released (features processing-2.0.2)

In recent weeks I've changed the default mode of ruby-processing from using the included jruby-complete by default to using an external (system installed) jruby. So the --jruby flag is now deprecated (it doesn't do anything) and is replaced by the --nojruby flag which causes the installed jruby-complete to be used instead of the system version. The jruby-complete is retained for the following uses:-

  1. To support application export
  2. To run certain sketches (mainly GLSL shader sketches) that wont run with installed jruby
  3. For people without an installed jruby (for whatever reason), make sure and use --nojruby flag.
Anyway I've also revisited, some of the samples with a view to introducing some more idiomatic ruby, and one thing I came up with was the idea to replace the heavyweight PVector class with a lightweight Struct alternative when the PVector class was being used as a repository for x, y, z values.
Vect = Struct.new(:x, :y, :z) do
  def add v
    self.x += v.x
    self.y += v.y
    self.z += v.z
  end
end

Monday 12 August 2013

Netbeans for ruby-processing

Hey this looks pretty promising, you can now install a plugin for netbeans-7.3 (for ruby and ruby-on-rails development). I will report how I get on install seemed to go ok, Once I figured out I needed to add *.jar as well as *.nbm sub-modules.

Wednesday 7 August 2013

Avoiding classpath conflict with jruby-complete

Well the simplest thing to do, would be ditch to jruby-complete, except:-
  1. Shader sketches still refuse to run. 
  2. We would lose export. 
  3. We would lose ability to install to system without requiring an installed jruby.
For me 1. is the biggest problem, any exported sketches will probably still be unable to use gems, also installing without jruby also means no access to gems.

See this gist for work in progress https://gist.github.com/monkstone/6172815. I propose to work on this independently on my monkstone branch will need updating from master. 


Motivation we could potentially use rubygems with P2D and P3D modes (even PImage sketches could not be run with rubygems). If we radically exclude jruby-complete until export gem would be much smaller.

Well it seem possible solution is to put jruby-complete.jar in its own directory, and possibly use:-

RUBY_PLATFORM == 'java'

So the solution is:-

Move ruby-complete.jar to its own folder (so we no-longer 'require jruby-complete.jar' in app.rb)

Dir["#{RP5_ROOT}/lib/core/\*.jar"].each { |jar| require jar }

Consequently we need to adjust the path jruby-complete.jar. And in the first place make sure we extract it to its new location easy. Seems to work, we retain the --jruby flag, which should be default unless we haven't installed jruby or we want to run shader sketches.

However it occurs to me this is the ideal opportunity to set using an external jruby as the default. Now all we need is a flag "--nojruby"  for when we need/prefer to use the included jruby-complete (which will remain handy to support export of applications). The shader sketches are the only ones now requiring such a flag. However it will also be required if ruby-processing gets installed without using system jruby (in this case you won't be able to use gems with ruby-processing). 

Well I've done it now since version 2.1.2 the default is to run ruby-processing from an external jruby, to revert to using installed jruby-complete now use the --nojruby flag (replaces the --jruby flag which had the opposite effect).

Tuesday 6 August 2013

Conways Game of Life in ruby-processing (featuring MDArray)

# game_of_life.rb featuring MDArray in ruby-processing
# A Processing implementation of Game of Life
# By Joan Soler-Adillon
#
# Press SPACE BAR to pause and change the cell's values with the mouse
# On pause, click to activate/deactivate cells
# Press R to randomly reset the cells' grid
# Press C to clear the cells' grid
#
# The original Game of Life was created by John Conway in 1970.
#

require 'mdarray'

CELL_SIZE = 5
ALIVE = true
DEAD = false
ALIVE_START = 150
WIDTH = 960
HEIGHT = 640
SKIP = 10
INTERVAL = 100

attr_reader :pause, :cells, :row, :column, :last_time, :alive, :cells_buffer

# signature-specific aliases for overloaded methods
java_alias :my_color, :color, [Java::float, Java::float, Java::float]
java_alias :my_fill, :fill, [Java::int]
java_alias :my_stroke, :stroke, [Java::float, Java::float]
java_alias :my_background, :background, [Java::int]

def setup
  size WIDTH, HEIGHT
  @row = WIDTH / CELL_SIZE
  @column = HEIGHT / CELL_SIZE
  stroke_weight 2
  @last_time = 0
  @pause = false
  @cells = MDArray.boolean([row, column], random_data)
  @alive = my_color(100, 255, 100)
  my_stroke(48, 100)
  no_smooth
end

def draw
  my_background(0)
  #Draw live cells
  (0 ... row).each do |x|
    (0 ... column).each do |y|
      if (cells.get([x, y]))
        my_fill(alive)
        rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
      end
    end
  end
  # Iterate if timer ticks
  if (millis - last_time > INTERVAL)
    if (!pause)
      tick!
      @last_time = millis
    end
  end

  # Create  new cells manually on pause
  if (pause && mouse_pressed?)
    # # Map and avoid out of bound errors
    x_cell_over = (map(mouse_x, 0, width, 0, row)).to_i
    x_cell_over = constrain(x_cell_over, 0, row - 1)
    y_cell_over = (map(mouse_y, 0, height, 0, column)).to_i
    y_cell_over = constrain(y_cell_over, 0, column - 1)

    # Check against cells in buffer
    if (cells_buffer.get([x_cell_over, y_cell_over]))  # Cell is alive
      cells.set([x_cell_over, y_cell_over], DEAD) # Kill
      my_fill(0) #reflect changed status
    else  # Cell is dead
      cells.set([x_cell_over, y_cell_over], ALIVE) # Make alive
      my_fill(alive) # Fill alive color
    end

  elsif (pause && !mouse_pressed?)  # And then save to buffer once mouse goes up
    # Save cells to buffer (so we operate with one array keeping the other intact)
    @cells_buffer = cells.copy
  end
end

def tick!  # When the clock ticks
  # Save cells to buffer (so we operate with one array keeping the other intact)
  @cells_buffer = cells.copy
  # Visit each cell:
  (0 ... row).each do |x|
    (0 ... column).each do |y|
      # And visit all the neighbours of each cell
      neighbours = 0 # We'll count the neighbours
      (x - 1 .. x + 1).each do |xx|
        (y - 1 .. y + 1).each do |yy|
          # Make sure you are not out of bounds
          if [(xx>=0), (xx<row), (yy>=0), (yy<column)].all? {|in_bounds| in_bounds == true}
            # Make sure to check against self
            if ![(xx == x), (yy == y)].all? {|is_self| is_self == true}
              if (cells_buffer.get([xx, yy])) # true == ALIVE
                neighbours += 1 # Check alive neighbours and count them
              end # alive
            end # End of if self
          end # End of if grid bounds
        end # End of yy loop
      end #End of xx loop
      # We've checked the neighbours: apply rules in one line (only in ruby)!
      cells.set([x, y], (cells_buffer.get([x, y]))?  ((2 .. 3) === neighbours) : (neighbours == 3))
    end # End of y loop
  end # End of x loop
end # End of function

def key_pressed
  case key
  when 'r', 'R'
    # Restart: reinitialization of cells 
    @cells = MDArray.boolean([row, column], random_data)
  when ' ' # On/off of pause
    @pause = !pause
  when 'c', 'C' # Clear all
    @cells = MDArray.boolean([row, column], DEAD)
  end
end

def random_data
  data = []
  (0 ... row * column).each do
    data << (rand(1000) < ALIVE_START)
  end
  return data
end

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])

Followers

Blog Archive

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