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

Showing posts with label daniel shiffman. Show all posts
Showing posts with label daniel shiffman. Show all posts

Monday, 10 March 2014

Yet Another Nature of Code Example ported to ruby-processing

For the nn library java code see ruby-processing on github.
# The Nature of Code
# Daniel Shiffman
# http://natureofcode.com

# XOR Multi-Layered Neural Network Example
# Neural network java code is all in the "src" folder
load_library :nn

require_relative './landscape'
include_package 'nn'

ITERATIONS_PER_FRAME = 5

attr_reader :inputs, :nn, :count, :land, :theta, :f, :result, :known


def setup

  size(400, 400, P3D)
  @theta = 0.0
  # Create a landscape object
  @land = Landscape.new(20, 300, 300)
  @f = create_font("Courier", 12, true)

  @nn = Network.new(2, 4)
  @count = 0
  # Create a list of 4 training inputs
  @inputs = []
  inputs << [1.0, 0]
  inputs << [0, 1.0]
  inputs << [1.0, 1.0]
  inputs << [0, 0.0]
end

def draw
  lights
  ITERATIONS_PER_FRAME.times do |i|
    inp = inputs.sample
    # Compute XOR    
    @known = ((inp[0] > 0.0 && inp[1] > 0.0) || (inp[0] < 1.0 && inp[1] < 1.0))? 0 : 1.0

    # Train that sucker!
    @result = nn.train(inp, known)
    @count += 1
  end

  # Ok, visualize the solution space
  background(175)
  push_matrix
  translate(width / 2, height / 2 + 20, -160)
  rotate_x(Math::PI / 3)
  rotate_z(theta)

  # Put a little BOX on screen
  push_matrix

  stroke(50)
  no_fill
  translate(-10, -10, 0)
  box(280)
  land.calculate(nn)
  land.render
  # Draw the landscape
  pop_matrix

  @theta += 0.0025
  pop_matrix

  # Display overal neural net stats
  network_status
end

def network_status
  mse = 0.0
  text_font(f)
  fill(0)
  text("Your friendly neighborhood neural network solving XOR.", 10, 20)
  text("Total iterations: #{count}", 10, 40)
  mse += (result - known) * (result - known)
  rmse = Math::sqrt(mse / 4.0)
  out = "Root mean squared error: #{format("%.5f", rmse)}"
  hint DISABLE_DEPTH_SORT
  text(out, 10, 60)
  hint ENABLE_DEPTH_SORT
end

# The Nature of Code
# Daniel Shiffman
# http://natureofcode.com

# "Landscape" example

class Landscape
  include Processing::Proxy

  attr_reader :scl, :w, :h, :rows, :cols, :z, :zoff



  def initialize(scl, w, h)
    @scl, @w, @h  = scl, w, h
    @cols = w / scl
    @rows = h / scl
    @z = Array.new(cols, Array.new(rows, 0.0))
  end


  # Calculate height values (based off a neural network)
  def calculate(nn)
    val = ->(curr, nn, x, y){curr * 0.95 + 0.05 *  (nn.feed_forward([x, y]) * 280.0 - 140.0)}
    @z = (0 ... cols).map{|i|
      (0 ... rows).map{|j|
        val.call(z[i][j], nn, i * 1.0/ cols, j * 1.0/cols)
      }
    }
  end

  # Render landscape as grid of quads
  def render
    # Every cell is an individual quad
    # (could use quad_strip here, but produces funny results, investigate this)
    (0 ... z.size - 1).each do |x|
      (0 ... z[0].size - 1).each do |y|

        # one quad at a time
        # each quad's color is determined by the height value at each vertex
        # (clean this part up)
        no_stroke
        push_matrix
        begin_shape(QUADS)
          translate(x * scl - w * 0.5, y * scl - h * 0.5, 0)
          fill(z[x][y]+127, 220)
          vertex(0, 0, z[x][y])
          fill(z[x+1][y]+127, 220)
          vertex(scl, 0, z[x+1][y])
          fill(z[x+1][y+1]+127, 220)
          vertex(scl, scl, z[x+1][y+1])
          fill(z[x][y+1]+127, 220)
          vertex(0, scl, z[x][y+1])
          end_shape
          pop_matrix
        end
      end
    end
  end

Sunday, 2 March 2014

Updating to use Dan Shiffmans new Box2D library

Caught this on twitter

So here is the liquidy.rb example updated:-
# The Nature of Code
# <http:#www.shiffman.net/teaching/nature>
# Spring 2011
# Updated to use the updated library
# translated to ruby-processing 2 March 2014 by Martin Prout
# Box2DProcessing example

load_libraries :box2d_processing, :particle_system


# module PS is a wrapper for java packages, and Boundary and Particle classes
include PS

attr_reader :box2d, :boundaries, :systems

def setup
  size(400,300)
  smooth
  # Initialize box2d physics and create the world
  @box2d = PS::Box2DProcessing.new(self)
  box2d.create_world
  # We are setting a custom gravity
  box2d.set_gravity(0, -20)
  # Create Arrays 
  @systems = []
  @boundaries = []
  # Add a bunch of fixed boundaries
  boundaries << Boundary.new(box2d, 50, 100, 300, 5, -0.3)
  boundaries << Boundary.new(box2d, 250, 175, 300, 5, 0.5)
end
def draw
  background(255)
  # We must always step through time!
  box2d.step
  # Run all the particle systems
  if systems.size > 0
    systems.each do |system|
      system.run
      system.add_particles(box2d, rand(0 .. 2))
    end
  end
  # Display all the boundaries
  boundaries.each do |wall|
    wall.display
  end
end
def mouse_pressed
  # Add a new Particle System whenever the mouse is clicked
  systems << ParticleSystem.new(box2d, 0, mouse_x, mouse_y)
end

Here is the particle_system library, particle_system.rb
module PS
  include_package 'org.jbox2d.collision.shapes'
  include_package 'org.jbox2d.common'
  include_package 'org.jbox2d.dynamics'
  include_package 'shiffman.box2d'

  # Box2D Particle System
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2010
  # translated to ruby-processing Martin Prout

  # A class to describe a group of Particles
  # An Array is used to manage the list of Particles 

  class ParticleSystem

    attr_reader :particles, :x, :y

    def initialize(bd, num, x, y)
      @particles = []          # Initialize the Array
      @x, @y = x, y            # Store the origin point  
      num.times do
        particles << PS::Particle.new(bd, x, y)
      end
    end

    def run
      # Display all the particles
      particles.each do |p|
        p.display
      end
      # Particles that leave the screen, we delete them
      # (note they have to be deleted from both the box2d world and our list

      particles.delete_if { |p| p.done}

    end

    def add_particles(bd, n)
      n.times do
        particles << PS::Particle.new(bd, x, y)
      end
    end

    # A method to test if the particle system still has particles
    def dead
      particles.empty?
    end

  end

  # The Nature of Code
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2012
  # PBox2D example

  # A Particle

  class Particle
    TRAIL_SIZE = 6
    # We need to keep track of a Body

    attr_reader :trail, :body, :box2d

    # Constructor
    def initialize(b2d, x, y)
      @box2d = b2d
      @trail = Array.new(TRAIL_SIZE, [x, y])

      # Add the box to the box2d world
      # Here's a little trick, let's make a tiny tiny radius
      # This way we have collisions, but they don't overwhelm the system
      make_body(PS::Vec2.new(x,y), 0.2)
    end

    # This function removes the particle from the box2d world
    def kill_body
      box2d.destroy_body(body)
    end

    # Is the particle ready for deletion?
    def done
      # Let's find the screen position of the particle
      pos = box2d.get_body_pixel_coord(body)
      # Is it off the bottom of the screen?
      if (pos.y > $app.height + 20)
        kill_body
        return true
      end
      return false
    end

    # Drawing the box
    def display
      # We look at each body and get its screen position
      pos = box2d.get_body_pixel_coord(body)

      # Keep track of a history of screen positions in an array
      (TRAIL_SIZE - 1).times do |i|
        trail[i] = trail[i + 1]
      end
      trail[TRAIL_SIZE - 1] = [pos.x, pos.y]

      # Draw particle as a trail
      begin_shape
        noFill
        stroke_weight(2)
        stroke(0,150)
        trail.each do |v|
          vertex(v[0], v[1])
        end
      end_shape
    end

    # This function adds the rectangle to the box2d world
    def make_body(center, r)
      # Define and create the body
      bd = PS::BodyDef.new
      bd.type = PS::BodyType::DYNAMIC

      bd.position.set(box2d.coord_pixels_to_world(center))
      @body = box2d.create_body(bd)

      # Give it some initial random velocity
      body.set_linear_velocity(PS::Vec2.new(rand(-1 .. 1), rand(-1 .. 1)))

      # Make the body's shape a circle
      cs = PS::CircleShape.new
      cs.m_radius = box2d.scalar_pixels_to_world(r)

      fd = PS::FixtureDef.new
      fd.shape = cs

      fd.density = 1
      fd.friction = 0  # Slippery when wet!
      fd.restitution = 0.5

      # We could use this if we want to turn collisions off
      #cd.filter.groupIndex = -10

      # Attach fixture to body
      body.create_fixture(fd)

    end

  end
  # The Nature of Code
  # <http://www.shiffman.net/teaching/nature>
  # Spring 2012
  # PBox2D example

  # A fixed boundary class (now incorporates angle)



  class Boundary

    attr_reader :box2d, :b, :x, :y, :w, :h #, :a

    def initialize(b2d, x, y, w, h, a)
      @box2d = b2d
      @x = x
      @y = y
      @w = w
      @h = h

      # Define the polygon
      sd = PS::PolygonShape.new

      # Figure out the box2d coordinates
      box2dW = box2d.scalar_pixels_to_world(w/2)
      box2dH = box2d.scalar_pixels_to_world(h/2)
      # We're just a box
      sd.set_as_box(box2dW, box2dH)


      # Create the body
      bd = PS::BodyDef.new
      bd.type = PS::BodyType::STATIC
      bd.angle = a
      bd.position.set(box2d.coord_pixels_to_world(x,y))
      @b = box2d.create_body(bd)

      # Attached the shape to the body using a Fixture
      b.create_fixture(sd,1)
    end

    # Draw the boundary, it doesn't move so we don't have to ask the Body for location
    def display
      fill(0)
      stroke(0)
      stroke_weight(1)
      rect_mode(CENTER)
      a = b.get_angle
      push_matrix
      translate(x,y)
      rotate(-a)
      rect(0,0,w,h)
      pop_matrix
    end
  end
end
It occurs to me, that with a bit more work and I could create a ruby-processing library to simplify the use of JBox2D? Thus replacing need for both fisica and box2d_processing libraries. Except that Ricard Marxer and Daniel Shiffman have both created some very nice examples. See other examples here.

Saturday, 21 September 2013

Reading and writing RubyStructs to yaml ruby processing

Since ruby 1.9.3 it is possible to read and write RubyStructs to YAML (using Psych), this makes for a more efficient way of storing and restoring data, whilst maintaining human readability see included yaml file. It is essential that the Struct is declared, and this is easiest done in our bubble library (see below).
######################################
# Yet another examples of reading and
# writing to some form of markup,
# appropriatetly yaml using ruby structs 
# by Martin Prout after Dan Shiffman
# ###################################
load_library :bubble

attr_reader :bubbles, :bubble_data


def setup()
  size(640, 360)
  # load data from file
  load_data
end

def draw
  background 255
  bubbles.each do|bubble|
    bubble.display
    bubble.rollover(mouse_x, mouse_y)
  end
end

def load_data
  yaml = Psych.load_file("data/struct_data.yaml")
  # we are storing the data as an array of RubyStruct, in a hash with
  # a symbol as the key (the latter only to show we can, it makes no sense)
  data = yaml[:bubbles]
  @bubbles = []
  # iterate the bubble_data array, and populate the array of bubbles
  data.each do |pt|
    bubbles << Bubble.new(pt.x, pt.y, pt.diameter, pt.label)
  end
end

def save_data
  # demonstrate how easy it is to create yaml object from a hash in ruby
  yaml = bubble_data.to_hash.to_yaml
  # overwite existing 'data.yaml' 
  open("data/struct_data.yaml", 'w') {|f| f.write(yaml) }
end

def mouse_pressed
  # create a new bubble instance, where mouse was clicked
  @bubble_data = BubbleData.new bubbles
  @bubble_data.add_bubble(Bubble.new(mouse_x, mouse_y, rand(40 .. 80), "new label"))
  save_data
  # reload the yaml data from the freshly created file
  load_data
end


class BubbleData
  attr_reader :data
  def initialize data
    @data = data
  end

  def add_bubble bubble
    data << bubble
    if (data.size > 10)
    # Delete the oldest bubble
      data.shift
    end
  end

  # Using symbol as hash key for a change.  Thus demonstrating only to show we can store 
  # more complex data strucures quite readily.
  def to_hash
    {bubbles: data.map{|point| point.to_struct}}
  end

end


The bubble library "library/bubble/bubble.rb"
# The bubble library, include BubbleStruct

class Bubble
  include Processing::Proxy

  attr_reader :x, :y, :diameter, :name, :over

  # Create  the Bubble
  def initialize(x, y, diameter, name)
    @x = x
    @y = y
    @diameter = diameter
    @name = name
    @over = false
  end

  # Checking if mouse is over the Bubble
  def rollover(px, py)
    d = dist(px,py,x,y)
    @over = (d < diameter/2)? true : false
  end

  # Display the Bubble
  def display
    stroke(0)
    stroke_weight(2)
    noFill
    ellipse(x,y,diameter,diameter)
    if (over)
      fill(0)
      text_align(CENTER)
      text(name, x, y + diameter/2 + 20)
    end
  end

  def to_struct
    BubbleStruct.new(x, y, diameter, name)
  end
end

BubbleStruct = Struct.new(:x, :y, :diameter, :label)

The data file "struct_data.yaml"
---
:bubbles:
- !ruby/struct:BubbleStruct
  x: 160
  y: 103
  diameter: 43.2
  label: Happy
- !ruby/struct:BubbleStruct
  x: 372
  y: 137
  diameter: 52.4
  label: Sad
- !ruby/struct:BubbleStruct
  x: 273
  y: 235
  diameter: 61.0
  label: Joyous
- !ruby/struct:BubbleStruct
  x: 121
  y: 179
  diameter: 44.7
  label: Melancholy

Monday, 16 September 2013

Using json in ruby-processing

It is actually far easier to use rubys json library than processing/java, because all you need is a hash to convert to_json, and vice versa. Here is one of Daniel Shiffmans sketches given the ruby-processing treatment. It might be also possible to do something similar with XML using Nokogiri (ruby hash to XML) and rails supports (XML to ruby hash).
require "json"

attr_reader :bubbles, :bubble_data

def setup()
  size(640, 360)
  # read source_string from file
  load_data
end

def draw
  background 255
  bubbles.each do|bubble|
    bubble.display
    bubble.rollover(mouse_x, mouse_y)
  end
end

def load_data
  source_string = open("data/data.json", "r").read
  # parse the source string
  @bubble_data = BubbleData.new

  # get the bubble_data from the top level hash
  data = bubble_data.extract_data source_string
  @bubbles = []
  # iterate the bubble_data array, and create an array of bubbles
  data.each do |point|
    bubbles << Bubble.new(
      point["position"]["x"],
      point["position"]["y"],
      point["diameter"],
      point["label"])
  end
end

def save_data
  # demonstrate how easy it is to create json object from a hash in ruby
  # json = bubble_data.to_hash.to_json # if you don't require pretty output
  json = JSON.pretty_generate(bubble_data.to_hash) # pretty output
  # overwite existing 'data.json' 
  open("data/data.json", 'w') {|f| f.write(json) }
end

def mouse_pressed
  # create a new bubble instance, where mouse was clicked
  @bubble_data.add_bubble(Bubble.new(mouse_x, mouse_y, rand(40 .. 80), "new label"))
  save_data
  # reload the json data from the freshly created file
  load_data
end

class BubbleData
  attr_reader :name, :data
  def initialize name = "bubbles"
    @name = name
    @data = []
  end

  def add_bubble bubble
    data << bubble
  end

  def extract_data source_string
    @data = JSON.parse(source_string)[name]
  end

  def to_hash
    {name => data.map{|point| point.to_hash}}
  end

end

class Bubble
  attr_reader :x, :y, :diameter, :name, :over

  def initialize(x, y, diameter, name)
    @x, @y, @diameter, @name = x, y, diameter, name
    @over = false
  end

  def rollover px, py
    d = dist px, py, x, y
    @over = (d < diameter / 2.0)
  end

  def display
    stroke 0
    stroke_weight 2
    no_fill
    ellipse x, y, diameter, diameter
    if over
      fill 0
      text_align CENTER
      text(name, x, y + diameter / 2.0 + 20)
    end
  end

  def to_hash
    {"position" => {"x" => x, "y" => y}, "diameter" => diameter, "label" => name}
  end
end


The data "data.json"
{
  "bubbles": [
      {
        "position": {
          "x": 160,
          "y": 103
        },
        "diameter": 43.19838,
        "label": "Happy"
      },
      {
        "position": {
          "x": 372,
          "y": 137
        },
        "diameter": 52.42526,
        "label": "Sad"
      },
      {
        "position": {
          "x": 273,
          "y": 235
        },
        "diameter": 61.14072,
        "label": "Joyous"
      },
      {
        "position": {
          "x": 121,
          "y": 179
        },
        "diameter": 44.758068,
        "label": "Melancholy"
      }
    ]
}

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