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

Showing posts with label control panel. Show all posts
Showing posts with label control panel. Show all posts

Thursday, 9 January 2014

Penrose tiling generator (after Shiffman)

Previously I have experimented with cfdg and lsystems to generate penrose tiling, this version by Dan Shiffman seems very similar to the cfdg version. Interestingly Dan ported this somewhat from python, and here we end with a ruby version. Dan unsurprisingly makes use of the PVector class to do the vector math here we use my Vec2D from the ruby-processing vecmath library, which is far more concise (designed to use arithmetic operators from the outset). Here is the sketch, somewhat drier than the original with choosable options via control panel (it is necessary to press "reset" to change seed/triangle type):-
# Penrose Tile Generator
# Using a variant of the "ArrayList" recursion technique: http://natureofcode.com/book/chapter-8-fractals/chapter08_section4
# Penrose Algorithm from: http://preshing.com/20110831/penrose-tiling-explained
# Daniel Shiffman May 2013
# Translated (and refactored) to ruby-processing Jan 2014 by Martin Prout

load_libraries :vecmath, :tile, :control_panel
attr_reader :tris, :s, :panel, :hide, :acute

def setup
  size(1024, 576)
  control_panel do |c|
    c.title = "Tiler Control"
    c.look_feel "Nimbus"
    c.checkbox  :seed
    c.checkbox  :acute
    c.button    :generate
    c.button    :reset!
    @panel = c
  end
  @hide = false
  init false # defaults to regular penrose
end

def draw
  # only make control_panel visible once, or again when hide is false
  unless hide
    @hide = true
    panel.setVisible(hide)
  end
  background(255)
  translate(width/2, height/2)
  tris.each do |t|
    t.display
  end
end

def generate
  next_level = []
  tris.each do |t|
    more = t.subdivide
    more.each do |m|
      next_level << m
    end
  end
  @tris = next_level
end

def reset!
  Tiler.acute(acute)  # set the Tiler first
  init @seed
  java.lang.System.gc # but does it do any good?
end

def init alt_seed
  @tris = []
  10.times do |i|     # create 36 degree segments
    a = Vec2D.new
    b = Vec2D.from_angle((2 * i - 1) * PI / 10)
    c = Vec2D.from_angle((2 * i + 1) * PI / 10)
    b *= 370
    c *= 370
    if alt_seed
      tile = (i % 2 == 0)? Tiler.tile(b, a, c) : Tiler.tile(c, a, b)
      tris << tile
    else
      tile = (i % 2 == 0)? Tiler.tile(a, b, c) : Tiler.tile(a, c, b)
      tris << tile
    end
  end
end

Here is the tile library:-
module Tiler
  @@acute = false

  def self.acute(x)
    @@acute = x
  end

  # setup the initial tiling with all red tiles
  def self.tile(a, b, c)
    tile = (@@acute)? ATile.new(0, a, b, c) : Tile.new(0, a, b, c)
  end
end

class Tile
  include Processing::Proxy
  PHI = (1.0 + Math.sqrt(5)) / 2.0 # golden ratio
  RED = [255, 0, 0]
  BLUE = [0, 0, 255]
  COLORS = [RED, BLUE]
  attr_reader :a, :b, :c, :col

  def initialize(col,  a,  b,  c)
    @col, @a, @b, @c = col, a, b, c
  end

  def display
    no_stroke
    fill(*COLORS[col])
    triangle(a.x, a.y, b.x, b.y, c.x, c.y)
    #fill(0,0,255)
    #ellipse(a.x,a.y,4,4)
    #ellipse(b.x,b.y,4,4)
    #ellipse(c.x,c.y,4,4)
  end

  def subdivide
    result = []
    if (col == 0)
      # Subdivide red triangle
      p = b - a
      p /= PHI
      p += a
      result << Tile.new(0, c, p, b)
      result << Tile.new(1, p, c, a)
    else
      # Subdivide blue triangle
      q = a - b
      q /= PHI
      q += b
      r = c - b
      r /= PHI
      r += b
      result << Tile.new(1, r, c, a)
      result << Tile.new(1, q, r, b)
      result << Tile.new(0, r, q, a)
    end
    return result
  end
end

class ATile < Tile
  def subdivide
    result = []
    if (col == 0)
      # Subdivide red (half kite) triangle
      q = b - a
      q /= PHI
      q += a
      r = c - b
      r /= PHI
      r += b
      result << ATile.new(1, r, q, b)
      result << ATile.new(0, q, a, r)
      result << ATile.new(0, c, a, r)
    else
      # Subdivide blue (half dart) triangle
      p = a - c
      p /= PHI
      p += c
      result << ATile.new(1, b, p, a)
      result << ATile.new(0, p, c, b)
    end
    return result
  end
end

Here is a screenshot, before I added second checkbox

Monday, 9 September 2013

New Improved Planetarium Basic Sketch

That is more like it decent controls, no need for that reflection nonsense. Another example using Andrés Colubri's planetarium library.
# The planetarium library is designed to create real-time projections on 
# spherical domes. It is based on the FullDome project by Christopher 
# Warnow (ch.warnow@gmx.de):
# https://github.com/mphasize/FullDome
#
# A brief descrition on how it works: a 360° view of the scene is generated
# by rendering the scene 6 times from each direction: positive x, negative x, 
# positive y, and so on. The output of each rendering is stored inside a cube map 
# texture, which is then applied on a sphere representing the dome.
# Hence, the library calls the draw method 6 times per frame in order to update  
# the corresponding side of the cube map texture (in reality, only 5 times since  
# the bottom side of the cube map is not invisible on the dome). 
# New improved version using control panel no need for reflection "nastiness"

load_libraries :planetarium, :control_panel
include_package 'codeanticode.planetarium'

attr_reader :cube_x, :cube_y, :cube_z, :panel, :hide

def setup
  # For the time being, only use square windows  
  size(600, 600, Dome::RENDERER)
  @hide = false
  control_panel do |c|
    c.title = "Control"
    c.look_feel "Metal"
    c.slider    :cube_x,  -width / 2.0 .. width / 2.0, 10.0
    c.slider    :cube_y,  -height / 2.0 .. height / 2.0, 10.0
    c.slider    :cube_z,  -width / 2.0 .. 0, -20.0
    @panel = c
  end
end

def mouse_pressed
  @hide = false if hide  # use mouse click to restore control panel
end

# Called five times per frame.
def draw
  unless hide
    @hide = true
    panel.set_visible(hide)
  end
  background(0)

  push_matrix
  translate(width/2, height/2, 300)

  lights

  stroke(0)
  fill(150)
  push_matrix
  translate(cube_x, cube_y, cube_z)
  box(50)
  pop_matrix

  stroke(255)
  lines_amount = 10
  (0 ... lines_amount).each do |i|
    ratio = i/(lines_amount - 1.0)
    line(0, 0, cos(ratio * TWO_PI) * 50, sin(ratio * TWO_PI) * 50)
  end
  pop_matrix
end

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

Tuesday, 28 August 2012

Control Panel Enhanced ArcBall Sketch

Here is an enhanced arcball sketch, features use of control panel for zoom facility, and drop down menu too choose which or whether rotation is frozen about an axis. See previous post for library.
load_libraries 'arcball', 'opengl', 'control_panel'
import "arcball"
import "opengl"

X = 0
Y = 1
Z = 2

attr_reader :my_ball, :zoom

def setup
  size(600, 600, OPENGL)
  setup_opengl
  @my_ball = ArcBall.new(width/2.0, height/2.0, min(width - 20, height - 20) * 0.5)
  control_panel do |c|
    c.title = 'Controller'
    c.slider  :zoom, 0..2.0, 0.0
    c.menu(:freeze, ['X-axis', 'Y-axis', 'Z-axis', 'free'], 'free') {|m| load_menu_item(m) }
  end
  @zoom = 0
end

def draw
  background(50, 50, 100)
  translate(width/2.0, height/2.0, zoom * width/2.0)
  define_lights
  update
  lights
  stroke(0)
  cube(my_ball.radius / 2.0)
end

def setup_opengl
  hint ENABLE_OPENGL_4X_SMOOTH     # optional
  hint DISABLE_OPENGL_ERROR_REPORT # optional
end

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 define_lights
  ambient(20, 20, 20)
  ambient_light(50, 50, 50)
  point_light(30, 30, 30, 200, -150, 0)
  directional_light(0, 30, 50, 1, 0, 0)
  spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, -0.5, PI / 2, 2)
end

def load_menu_item(m)
  case(m)
  when 'X-axis':
    my_ball.select_axis(X)
  when 'Y-axis':
    my_ball.select_axis(Y)
  when 'Z-axis':
    my_ball.select_axis(Z)
  when 'free'
    my_ball.select_axis(-1)
  end
end

def cube(sz)
  sz *= 0.5
  fill(200,  200,  200,  255)
  begin_shape(QUADS)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, +sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, -sz, -sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(+sz, +sz, +sz)
    vertex(+sz, +sz, -sz)
    vertex(-sz, -sz, -sz)
    vertex(+sz, -sz, -sz)
    vertex(+sz, -sz, +sz)
    vertex(-sz, -sz, +sz)
    vertex(-sz, +sz, -sz)
    vertex(+sz, +sz, -sz)
    vertex(+sz, +sz, +sz)
    vertex(-sz, +sz, +sz)
    end_shape
end

sketch screenshot using the gimp

Sunday, 17 June 2012

A Gui for my pixelation library

Over on my other blog I recently described an application for pixelating pictures in processing, but also using the cfdg program (CF3), here is the program adapted for ruby-processing, with an added gui using "control_panel"




load_libraries 'pde2cfdg', 'control_panel'
import "pde2cfdg"

#
# Two cfdg files are written to the sketch folder one for design one for data,
# this means it easier to edit the design (eg to replace CIRCLE with a custom shape)
# The output png file is written to the sketch folder,
#

attr_reader :cfdg, :dot_size

def setup
    size(640, 512)
    background(255)
    setup_panel
    colorMode(HSB, 1.0)
    @cfdg = ProcessingToCF.new(self)
    cfdg.set_dot_size(dot_size)
    cfdg.setPathToCFDG("/home/tux/CF3/cfdg")
    cfdg.get_input
    cfdg.writeCFDG
end

def setup_panel
    control_panel do |c|
      c.title = "Control:"
      c.slider(:dot_size, 1..6, 3)
      c.button(:re_run)
    end
end

def re_run
  cfdg.set_dot_size(dot_size)
  cfdg.writeCFDG
end

def draw
  if (cfdg.finished)
    img = load_image(cfdg.outFile)
    image(img, 0, 0, img.width, img.height)
  end
end
Here's a screenshot of application in action, the artist is Phil Sutton RA

Saturday, 4 February 2012

Using the Control Panel With Toxiclibs

Here is a recent sketch modified to use the ruby processing control panel (doesn't want to work with opengl with current release), next obvious variant to include sliders for resolution and or upper and lower boundary levels.
#
# This example implements a custom VolumetricSpace using an implicit function
# to calculate each voxel. This is slower than the default array or HashMap
# based implementations, but also has much less memory requirements and so might
# be an interesting and more viable approach for very highres voxel spaces
# (e.g. >32 million voxels). This implementation here also demonstrates how to
# achieve an upper boundary on the iso value (in addition to the one given and
# acting as lower threshold when computing the iso surface)
#
# Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012
# This sketch relies on a custom toxiclibscore library for PovRAY export, and
# features the ruby-processing control panel (doesn't seem to work with opengl)
# This library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
# 
# http://creativecommons.org/licenses/LGPL/2.1/
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

load_libraries 'toxiclibscore', 'toxiclibs_p5', 'volumeutils', 'control_panel'
include_package 'toxi.geom'
include_package 'toxi.geom.mesh'
include_package 'toxi.volume'
include_package 'toxi.processing'

RES = 64
ISO = 0.2
MAX_ISO = 0.66

attr_reader :mesh, :gfx, :is_wire_frame, :curr_zoom

def setup()
  size(720,720, P3D)
  @curr_zoom = 1.0
  control_panel do |c|
    c.title = "Implicit Function"
    c.slider :curr_zoom, 0.5..4.0, 1.0
    c.button :wire_frame_view
    c.button :smooth
    c.button :save_sketch_view
    c.button :export_to_povray
    c.button :exit!
  end
  @gfx = ToxiclibsSupport.new(self)
  vol = EvaluatingVolume.new(Vec3D.new(400,400,400), RES, RES, RES, MAX_ISO)
  surface = HashIsoSurface.new(vol)
  @mesh = WETriangleMesh.new()
  surface.compute_surface_mesh(mesh, ISO)
  @is_wire_frame = false
end

def draw()
  background(0)
  translate(width / 2.0, height / 2.0, 0)
  rotate_x(mouse_y * 0.01)
  rotate_y(mouse_x * 0.01)
  scale(curr_zoom)
  if (is_wire_frame)
    no_fill()
    stroke(255)
  else
    fill(255)
    no_stroke()
    lights()
  end
  @gfx.mesh(mesh, true)
end

def wire_frame_view
  @is_wire_frame = !is_wire_frame
end

def smooth
  LaplacianSmooth.new().filter(mesh, 1)
end

def export_to_povray
  file_id = "implicit"
  pw = create_writer(file_id + ".inc")
  mesh.saveAsPOV(pw)
  pw.flush()
  pw.close()
  exit()
end

def save_sketch_view
  save_frame("implicit.png")
end

class EvaluatingVolume < VolumetricSpace
  include Processing::Proxy
  include_package 'toxi.math'
  attr_reader :upper_bound, :lut
  FREQ = PI * 3.8

  def initialize(scal_vec, resX, resY, resZ, upper_limit)
    super(scal_vec, resX, resY, resZ)
    @upper_bound = upper_limit
    @lut=SinCosLUT.new()
  end

  def clear()
    # nothing to do here
  end

  def getVoxelAt(i)
    getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes)
  end

  def getVoxel(x, y, z)  # can't overload so we renamed
    val = 0
    if (x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1)
      xx = x * 1.0 / resX - 0.5  # NB: careful about integer division !!!
      yy = y * 1.0 / resY - 0.5
      zz = z * 1.0 / resZ - 0.5
      #val = lut.sin(xx * FREQ) + lut.cos(yy * FREQ) + lut.sin(zz * FREQ)
      val = lut.sin(xx * FREQ) * (xx * FREQ) + lut.sin(yy * FREQ) * (yy * FREQ) + lut.sin(zz * FREQ) * (zz * FREQ)
      #val = lut.cos(xx * FREQ) * lut.sin(yy* FREQ) + lut.cos(yy* FREQ) * lut.sin(zz* FREQ) + lut.cos(zz* FREQ)* lut.sin(xx* FREQ);
      if (val > upper_bound)
        val = 0
      end
    end
    return val
  end
end
Screenshot of ruby processing sketch

Wednesday, 16 November 2011

Exporting Sketches from Ruby-Processing to PovRAY, features control panel

It has taken a lot longer that it should have done, but finally I got round to exploring my vanilla processing povwriter library in ruby processing. Thanks to the latest version of ruby-processing, we can use the vanilla processing library from the sketchbook folder. This works fine and dandy at present, but I can foresee potential conflicts with libraries that have been modified to run with processing-2.0, but then lots of libraries are probably going to get screwed along the way to the new version (my povwriter library for one does not work with processing-2.0). Here's one of my povwriter example files translated to ruby:-



# Demonstrates how you can use my povwriter libary
# in Ruby-Processing, to export to PovRAY

load_libraries 'povwriter', 'control_panel'
import 'povexport'

DATA = [-1, 0, 1]
attr_reader :exporter, :precord, :pquality, :type, :pview, :memory, :povfile

def setup
  size 300, 300, P3D
  @povfile = "balls.pov"
  @precord = false
  @exporter = PovExporter.new self
  control_panel do |c|
    c.title = "Render as PovRAY"
    c.menu(:quality, ['low', 'medium', 'high', 'highest'], 'high') {|m| load_quality_choice(m) }
    c.menu(:image_type, ['png', 'jpeg', 'tga'], 'png') {|m| load_image_type(m) }
    c.menu(:memory, ['low', 'default', 'high', 'none'], 'default') {|m| load_memory_choice(m) }
    c.checkbox :antialias
    c.button :choose_template
    c.button :record
    c.button :raytrace
    c.button :view
    c.button :exit!
  end
  # uncomment next enter the path to povray executable on mac/windows/linux (or possibly jedit linux)  
  exporter.set_povray_path('/usr/local/bin/povray') # choice is saved in '.povwriter/povwriter.properties'
  # uncomment following line to adjust degenerate triangle cut off (area squared)
  # export.set_epsilon("0.001")
  exporter.store_settings    # uncomment this line to save settings   
  no_stroke
  sphere_detail(18)
end

def draw
  lights          # this needs to be outside the record loop
  if (precord)
    no_lights   # let PovRAY do the lighting
    no_loop       # don't loop while recording sketch
    begin_raw("povexport.RawPovray", data_path(povfile))
  end
  if pview
    background(0) # clear the screen
    image(load_image(data_path("balls.png")), 0, 0)
    no_loop
  else
    render
  end
  if (precord)
    end_raw
    @precord = false
    puts "done recording"
    loop
  end
end

def exit!
  exit
end

def render()
  background(0)
  translate(width*0.7, height/2, -width)
  DATA.each do |y|
    DATA.each do |x|
      DATA.each do |z|
        push_matrix
        translate(120*x, 120*y, 120*z)
        fill(rand * 255, rand * 255, rand * 255) # a nice test for my colorFactory class
        exporter.sphere(60) # use a better sphere primitive for ray tracing
        pop_matrix
      end
    end
  end
end

################################### Independent of sketch ############################
def record
  @precord = true
end

def raytrace
  Kernel.system([exporter.get_povray_path, povray_options, povray_scene].join(" "))
end

def choose_template
  exporter.choose_template
end

def view
  @pview = true
end

def povray_scene
  return "+I#{data_path(povfile)}"
end

def povray_options
  aa = @antialias ? "+A" : ""
  return "+IM#{memory} +W#{width} +H#{height} +Q#{pquality} #{aa} +F#{type}"
end


def load_quality_choice(item)
  map = {'low' => 0, 'medium' => 5, 'high' => 9, 'highest' => 11}
  @pquality = map[item]
end

def load_memory_choice(item)
  map = {'low' => 56, 'default' => 128, 'high' => 1024, 'none' => 0}
  @memory = map[item]
end

def load_image_type(item)
  map = {'png' => 'N', 'jpeg' => 'J', 'tga' => 'C'}
  @type = map[item]
end

Sketch now modified to use the excellent control panel of ruby-processing. Here I am using a custom sphere for raytracing, ideally I would just call the PovRAY sphere primitive, but that is a bit more complicated than it appears at first sight (see my vanilla processing blog). Presently everything is exported as a simple triangle mesh (ideally this would be a PovRAY mesh2 object). Make life easy for yourself and wrap drawing logic in a function such as render. Click on images below to see full size....

Live Display of PovRAY rendering

Viewing Rendered image in processing sketch



Thursday, 16 June 2011

Faux Fish Eye Lens in ruby-processing, features control panel

Sketches below show an unadulterated grid, a partial grid with fish eye 'filter' and some text with fish eye 'filter'. Unfortunately the mouse was doing two things (adjusting view and selecting image for 'the gimp') so actual captured image was a bit hit and miss. View the vanilla processing version at openprocessing.


############################
# fish_eye.rb 
# [Pseudo fisheye simulation]
# by Jonsku, September 2010
# From the article by Paul Bourke : http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/domefisheye/fisheye/
#
# translated to ruby-processing by Martin Prout (June 2011) 
# Toggle fisheye, select view, adjust aperture using 'Control Panel'
# Move mouse to change camera position.
############################

require 'beauty'

class Fish_Eye < Processing::App
  load_library :control_panel
  attr_reader :b_pass, :fish_eye_filter, :aperture, :options, :some_text, :img

  def setup()
    size(300, 300)
    setup_control
    text_font(load_font("Ubuntu-Regular-48.vlw"))
    @some_text = "Whatever it is,\n it was inside the Sphere.\n Now it's out, free to act. "
    @img = load_image("pattern.png")
    @fish_eye_filter = false
    @b_pass = BeautyPass.new
    smooth
  end

  def draw()
    background(0)
    case options
    when 'grid'
      draw_grid(20)
    when 'text'
      draw_text
    when 'image'
      display_image
    when 'noise'
      draw_noise
    else
      draw_grid(20)
    end
    if fish_eye_filter
      fisheye
    end
  end

  def setup_control
    control_panel do |c|
      c.title = "Control Panel"
      c.button :t_fisheye
      c.menu(:options, ['grid', 'text', 'image', 'noise' ], 'grid')
      c.slider :aperture, 0.5..Math::PI, 3.142
    end
  end

  def t_fisheye
    @fish_eye_filter = !fish_eye_filter
  end

  def draw_grid(row)
    stroke(255)
    stroke_weight(2)
    for i in 0...row
      line(0, i * height / row, width, i * height / row)
      line(i * height / row, 0, i * height / row, height)
    end
  end

  def draw_text
    fill(0, 255, 0)
    text(some_text,sin(frame_count * 0.1)*(width - text_width(some_text)), 180+Math.sin(Math::PI/2 + frame_count*0.01)*(height-170))
  end

  def display_image
    image(img, 0, 0, width, height)
  end

  def draw_noise
    push_style
    color_mode(RGB,1.0)
    load_pixels
    (0...width).each do |i|
      (0...height).each do |j|
        pixels[i+j*width] = color(noise((frame_count+i)*0.1,j*0.1),noise((frame_count+i)*0.1,j*0.1,0.3),noise((frame_count+i)*0.05,j*0.05,0.6))
      end
    end
    update_pixels
    pop_style
  end

  def fisheye
    half_aperture = aperture / 2
    # define camera positioning normalized coordinates
    cam_x = (2.0 * mouse_x) / (width - 1)
    cam_y = (2.0 * mouse_y) / (width - 1)

    load_pixels
    (0...width).each do |i|
      #transform pixel coordinates to normalised coordinates, range -1 to 1
      x = (2.0 * i) / (width - 1)
      (0...height).each do |j|
        y = (2.0 * j) / (height - 1)
        #radius r to camera
        r = dist(cam_x, cam_y, x, y)
        #at this stage any pixels where r > 1 are ignored
        if (r.abs > 1)
          next
        end
        #angle phi to x axis
        phi = Math.atan2(y - cam_y, x - cam_x)
        #r is mapped onto theta and phi is used directly as the polar coordinates of the direction vector 
        # from the camera into the scene
        theta = half_aperture * r
        #transform normalised coordinates to pixel coordinates
        dx = map(Math.sin(theta) * Math.cos(phi), -1.0, 1, 0, width)
        dy = map(Math.sin(theta) * Math.sin(phi), -1.0, 1, 0, height)

        ##############
        # Because the pixel array uses integer coordinates, black empty pixels will appear in the resulting image.
        # To attempt to fix this, the destination pixel is calculated with 3x3 combinations of roundings (round-round, 
        # the final value will be the average colors. See BeautyPass, it will become clearer.
        # round.floor, round.ceil, floor.floor, floor-round, etc.). Thus one pixel might have several colors but that 
        # is taken into account and NOTE: This works ok for aperture up to 180 but above that it is not enough.
        ###############
        fx = dx.round
        fy = dy.round
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.round
        fy = dy.floor
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.round
        fy = dy.ceil
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.floor
        fy = dy.floor
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.floor
        fy = dy.round
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.floor
        fy = dy.ceil
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.ceil
        fy = dy.ceil
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.ceil
        fy = dy.round
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
        fx = dx.ceil
        fy = dy.floor
        @b_pass.add_color(fx + fy * width, pixels[(i + j * width)])
      end
    end
    @b_pass.render
    @b_pass.reset
  end

end


############################
# beauty.rb (helper class), part of
# --=[Angular fisheye simulation]=--
# by Jonsku, September 2010
# From the article by Paul Bourke : http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/domefisheye/fisheye/
#
# translated to ruby-processing by Martin Prout
#
# Move mouse to change camera position.
############################
class BeautyPass
  include Processing::Proxy

  attr_reader :counts, :rSums, :gSums, :bSums

  def initialize()
    reset()
  end

  def reset()
    @counts = Array.new(width * height, 0)
    @rSums = Array.new(width * height, 0)
    @gSums = Array.new(width * height, 0)
    @bSums = Array.new(width * height, 0)
  end

  def add_color(i, c)
    if (i>0 && i< width * height)
      @rSums[i] += c >> 16 & 0xFF
      @gSums[i] += c >> 8 & 0xFF
      @bSums[i] += c & 0xFF
      @counts[i] +=  1
    end
  end

  def render()
    load_pixels()
    (0...width * height).each do |i|
      if (counts[i]>0)
        pixels[i] = color(rSums[i] / counts[i], gSums[i] / counts[i], bSums[i] / counts[i])
      else
        pixels[i] = color(0) #background color
      end
    end
    update_pixels()
  end
end





Saturday, 4 September 2010

Using the control panel and Toxi Processing Libraries in ruby processing

Here is a sketch that shows how easy it is to add controls to your sketch in ruby-processing.


#
# <p>GrayScottControl uses the seedImage() method to use a bitmap as simulation seed.
# In this demo the image is re-applied every frame and the user can adjust the
# F coefficient of the reaction diffusion to produce different patterns emerging
# from the boundary of the bitmapped seed. Unlike some other GS demos provided,
# this one also uses a wrapped simulation space, creating tiled patterns.</p>
#
# <p><strong>usage:</strong></p>
# <ul>
# <li>click + drag mouse to locally disturb the simulation</li>
# <li>Use slider to adjust the F parameter of the simulation</li>
# <li>press reset! to do what?</li>
# <li>press save_graycsott to do save</li>
# </ul>
#

#
# Copyright (c) 2010 Karsten Schmidt (rubified by Martin Prout)
#
# This demo & library is free software you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation either
# version 2.1 of the License, or (at your option) any later version.
#
# http:#creativecommons.org/licenses/LGPL/2.1/
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

class GrayScottControl < Processing::App
  load_libraries 'simutils','toxiclibscore','colorutils','control_panel'
  include_package "toxi.sim.grayscott"
  include_package "toxi.math"
  include_package "toxi.color"

  attr_reader :gs, :tone_map, :img, :set_f

  def setup
    size 256, 256
    setup_control
    @gs = GrayScott.new width, height, true
    @img = load_image "two_prong.png"
    # create a duo-tone gradient map with 256 steps
    # NB: use '::' in place of '.' here
    @tone_map = ToneMap.new(0, 0.33, NamedColor::CRIMSON, NamedColor::WHITE, 256)
  end

  def draw
    @gs.seed_image(img.pixels, img.width, img.height)
    @gs.set_rect(mouse_x, mouse_y, 20, 20) if mouse_pressed?
    load_pixels
    10.times { @gs.update(1) }
    # read out the V result array
    # and use tone map to render colours
    gs.v.length.times do |i|
      pixels[i]=tone_map.getARGBToneFor(gs.v[i])  # NB: don't camel case convert here
    end
    @gs.setF(0.02 + set_f * 0.001)
    update_pixels
  end

  def setup_control
    control_panel do |c|
      c.title = "Control Panel"
      c.slider :set_f, 0..9, 8
      c.button :save_graycsott
      c.button :reset!
    end
  end

  def save_graycsott
    no_loop
    save_frame "toxi.png"
    loop
  end

  def reset!
    @gs.reset
  end

end




Monday, 26 April 2010

Using the Voronoi library in ruby processing

This something I knocked up after reading a posting on the processing discourse (exhibition) posted by "guyy". The code is a lot simpler in ruby, and I much prefer the ruby processing control panel to controlP5 gui. You can get the voronoi mesh library here. In my revised version as of 30 April 2010, you can load a different image to work with.


load_libraries 'mesh', 'control_panel'
import 'megamu.mesh.Voronoi'

attr_reader :voronoi, :img, :ready, :upper, :lower, :lowskip, :highskip, :show, :points, :load_file

def setup()
 size(600, 600)
 frame_rate(10)
 #no_cursor()
 @ready = false
 @show = false
 @load_file = true
 setup_control
 @points = Array.new
 puts("done setup")
end

def setup_control
  control_panel do |c|
    c.title = "Control Panel"
    c.slider :upper, 50..255, 200
    c.slider :lower, 0..205, 150
    c.slider :lowskip, 1..20, 1
    c.slider :highskip, 1..20, 7
    c.button :change_image
    c.button :Go
    c.button :view_image
    c.button :save_voronoi
  end
end

def draw()
  background(0)
  stroke(128)
  if (load_file)
    no_loop
    file = select_input("Choose Image File")
    @img = load_image(file)
    @load_file = false
    loop
  end
  if (ready)
    display_voronoi
  end
  if(show && img)
    tint(255,100)
    image(img,0,0)
  end
end

def get_all_points()
  x_array = Array.new  # random array of x points
  y_array = Array.new  # random array of y points
  x = 0
  y = 0

  while(x < (width - highskip))
    x_array.push(x)
    x += rand(highskip) + lowskip
  end

  while(y < (height - highskip))
    y_array.push(y)
    y += rand(highskip) + lowskip
  end

  x_array.each do |pos_x|
    y_array.each do |pos_y|
      b = brightness(img.get(pos_x, pos_y))
      if (b <= upper && b >= lower)
        @points.push([pos_x, pos_y])
      end
    end
  end
  puts("total #{points.size()}")
end

def display_voronoi
  regions = voronoi.get_regions
  stroke(128)
  fill(0)  
  regions.each do |region|
    region_coordinates = region.get_coords
    region.draw(self)
  end

  edges = voronoi.get_edges
  
  edges.each do |edge|
    x0 = edge[0]
    y0 = edge[1]
    x1 = edge[2]
    y1 = edge[3]
    line(x0, y0, x1, y1)
  end

  fill(128)
  no_stroke
end

def mouse_pressed()
  no_loop
  b = brightness(img.get(mouse_x, mouse_y)) if img
  puts "Brightness = #{b}"
  loop
end

def Go()
  no_loop
  @ready = false
  @points.clear
  get_all_points if img
  @voronoi = Voronoi.new(@points.to_java(Java::float[]))
  @ready = true
  loop
end

def save_voronoi
  no_loop
  save_frame
  puts "finished!"
  loop
end

def change_image
  @load_file = !load_file      
end
def view_image
  @show = !show
end


Use this tool to create "voronoi portraits" from greyscale images. Here is the application in action with one of my context free "tree" images. In the screenshot below the view_image button has been selected, after the the voronoi was displayed.



If you move the mouse over regions in the original image, and press it the brightness is printed to the console (helps setting thresholds).

Here is what a saved voronoi image looks like (images are saved as a tif by default).


Here is an image you might recognize (Inverted using the Gimp)








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