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

Showing posts with label lambda. Show all posts
Showing posts with label lambda. Show all posts

Thursday, 15 May 2014

Lambda clock (featuring DegLut tables now only in JRubyArt, soon ruby-processing)

There exists this vanilla processing example, which is ideal for our degree sin/cos lookup tables (and a more functional approach in ruby processing using lambda). Basically if you can do the math then I think you should (rather than hide behind dubious vanilla processing map/normalize utility they are not helping you they are hindering your brain).
# The current time can be read with the second(), minute(), 
# and hour() functions. In this example, DegLut.sin() and DegLut.cos() values
# are used to set the position of the hands, perfect for degree precision Lookup Table.
load_library :fastmath

def setup
  size 200, 200
  stroke 255
  smooth 8
end

def draw
  background 0
  fill 80
  no_stroke
  # adj factor to map 0-60 to 0-360 (seconds/minutes) & 0-12 to 0-360 (hours)
  # since angles for DegLut.sin() and DegLut.cos() start at 3 o'clock we
  # subtract 90 degrees to make them start at the top.
  clock_x = ->(val, adj, length){DegLut.cos((val * adj).to_i - 90) * length + width / 2}
  clock_y = ->(val, adj, length){DegLut.sin((val * adj).to_i - 90) * length + height / 2}
  ellipse 100, 100, 160, 160
  stroke 220
  stroke_weight 6
  line( 100, 100, clock_x.call(hour % 12 + (minute / 60.0), 30, 50), clock_y.call(hour % 12 + (minute / 60.0), 30, 50) )
  stroke_weight 3
  line( 100, 100, clock_x.call(minute + (second / 60.0), 6, 60), clock_y.call(minute + (second / 60.0), 6, 60) )
  stroke 255, 0, 0
  stroke_weight 1
  line( 100, 100, clock_x.call(second, 6, 72), clock_y.call(second, 6, 72) )
  # Draw the minute ticks
  stroke_weight 2
  stroke 255
  (0..360).step(6) do |a|
    x = 100 + DegLut.cos(a) * 72
    y = 100 + DegLut.sin(a) * 72
    point x, y
  end
end

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

Tuesday, 6 July 2010

3D Gears: (exploring ruby 1.9 syntax, ruby-opengl bindings, lambda & Struct)

Recently I have been exploring a couple of Scheme implementations of cfdg, one of those used opengl as a back end. That got me interested in the idea of exploring ruby-opengl bindings and this is the result (I found this rather old example of glxgears by Arto Bendiken, which was in need of updating anyway, the array entry for color and light attributes are now Struct rather than array). I got a bit carried away as I've replaced the callback methods with new stabby ruby lambda syntax, anyway result is rather impressive. Main thing bugging is I can't give the window a title (the escape key now works for me, since I replace 27 with ?\e).


#!/usr/bin/env ruby1.9
# coding: utf-8

# 3-D gear wheels. This program is in the public domain.
#
# Command line options:
#    -info      print GL implementation information
#    -exit      automatically exit after 30 seconds
#
# 2005-05-01 Ruby version by Arto Bendiken based on gears.c rev 1.8.
# 2005-01-09 Original C version (gears.c) by Brian Paul et al.
# http://cvs.freedesktop.org/mesa/Mesa/progs/demos/gears.c?rev=1.8
# 2010-06-05 Revised Ruby Version Monkstone tested on AMD 64 bit linux with nvidia driver
# using lib-opengl-ruby1.9 (ie not the gem version)

require 'opengl'

class Gears
  attr_reader :autoexit, :frames, :t0_idle, :t0
  
  Position = Struct.new("Position", :x, :y, :z, :w)   # Struct now replace Array
  Color = Struct.new("Color", :red, :green, :blue, :alpha)
  
  POS = Position.new(5.0, 5.0, 10.0, 0.0)  
  RED = Color.new(0.8, 0.1, 0.0, 1.0)
  GREEN = Color.new(0.0, 0.8, 0.2, 1.0)
  BLUE = Color.new(0.2, 0.2, 1.0, 1.0)
  
  include GL
  include GLUT
  include Math
 

  # Draw a gear wheel. You'll probably want to call this function when
  # building a display list since we do a lot of trig here.
  #
  # Input:  inner_radius - radius of hole at center
  #         outer_radius - radius at center of teeth
  #         width - width of gear
  #         teeth - number of teeth
  #         tooth_depth - depth of tooth
  def gear(inner_radius, outer_radius, width, teeth, tooth_depth)
    r0 = inner_radius
    r1 = outer_radius - tooth_depth / 2.0
    r2 = outer_radius + tooth_depth / 2.0

    da = 2.0 * PI / teeth / 4.0

    ShadeModel(GL::FLAT)

    Normal(0.0, 0.0, 1.0)

    # Draw front face
    Begin(GL::QUAD_STRIP)
    (0..teeth).each do |i|
      angle = i * 2.0 * PI / teeth
      Vertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
      Vertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
      if i < teeth
        Vertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
        Vertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5)
      end
    end
    End()
  
    # Draw front sides of teeth
    Begin(GL::QUADS)
    (0...teeth).each do |i|
      angle = i * 2.0 * PI / teeth
      Vertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
      Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5)
      Vertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5)
      Vertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5)
    end
    End()
  
    Normal(0.0, 0.0, -1.0)
  
    # Draw back face
    Begin(GL::QUAD_STRIP)
    (0..teeth).each do |i|
      angle = i * 2.0 * PI / teeth
      Vertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
      Vertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
      if i < teeth
        Vertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5)
        Vertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
      end
    end
    End()
  
    # Draw back sides of teeth
    Begin(GL::QUADS)
    (0...teeth).each do |i|
      angle = i * 2.0 * PI / teeth
      Vertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5)
      Vertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5)
      Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5)
      Vertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
    end
    End()
  
    # Draw outward faces of teeth
    Begin(GL::QUAD_STRIP)
    (0...teeth).each do |i|
      angle = i * 2.0 * PI / teeth
      Vertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5)
      Vertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5)
      u = r2 * cos(angle + da) - r1 * cos(angle)
      v = r2 * sin(angle + da) - r1 * sin(angle)
      len = sqrt(u * u + v * v)
      u /= len
      v /= len
      Normal(v, -u, 0.0)
      Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5)
      Vertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5)
      Normal(cos(angle), sin(angle), 0.0)
      Vertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5)
      Vertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5)
      u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da)
      v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da)
      Normal(v, -u, 0.0)
      Vertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5)
      Vertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5)
      Normal(cos(angle), sin(angle), 0.0)
    end
    Vertex3f(r1 * cos(0), r1 * sin(0), width * 0.5)
    Vertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5)
    End()
  
    ShadeModel(GL::SMOOTH)
  
    # Draw inside radius cylinder
    Begin(GL::QUAD_STRIP)
    (0..teeth).each do |i|
      angle = i * 2.0 * PI / teeth
      Normal(-cos(angle), -sin(angle), 0.0)
      Vertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5)
      Vertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5)
    end
    End()
  end

  def init
    @angle = 0.0
    @view_rotx, @view_roty, @view_rotz = 20.0, 30.0, 0.0

    Lightfv(GL::LIGHT0, GL::POSITION, POS)
    Enable(GL::CULL_FACE)
    Enable(GL::LIGHTING)
    Enable(GL::LIGHT0)
    Enable(GL::DEPTH_TEST)

    # Make the gears
    @gear1 = GenLists(1)
    NewList(@gear1, GL::COMPILE)
    Material(GL::FRONT, GL::AMBIENT_AND_DIFFUSE, RED)
    gear(1.0, 4.0, 1.0, 20, 0.7)
    EndList()

    @gear2 = GenLists(1)
    NewList(@gear2, GL::COMPILE)
    Material(GL::FRONT, GL::AMBIENT_AND_DIFFUSE, GREEN)
    gear(0.5, 2.0, 2.0, 10, 0.7)
    EndList()

    @gear3 = GenLists(1)
    NewList(@gear3, GL::COMPILE)
    Material(GL::FRONT, GL::AMBIENT_AND_DIFFUSE, BLUE)
    gear(1.3, 2.0, 0.5, 10, 0.7)
    EndList()

    Enable(GL::NORMALIZE)

   ARGV.each do |arg|
      case arg
        when '-info'
          puts "GL_RENDERER   = #{GetString(GL::RENDERER)}"
          puts "GL_VERSION    = #{GetString(GL::VERSION)}"
          puts "GL_VENDOR     = #{GetString(GL::VENDOR)}"
          puts "GL_EXTENSIONS = #{GetString(GL::EXTENSIONS)}"
        when '-exit'
          @autoexit = 30
          puts "Auto Exit after #{@autoexit} seconds. " 
      end
    end
  end

  def initialize
    Init()
    InitDisplayMode(GLUT::RGB | GLUT::DEPTH | GLUT::DOUBLE)
    InitWindowPosition(0, 0)
    InitWindowSize(300, 300)    
    CreateWindow([]) # want's C char array as entry
    init()
    glut_callbacks()
    DisplayFunc(@draw)
    ReshapeFunc(@reshape)
    KeyboardFunc(@key)
    SpecialFunc(@special)
    VisibilityFunc(@visible)
    MouseFunc(@mouse)
    MotionFunc(@motion)
  end

  def start
    MainLoop()
  end
  
end

private
 
def glut_callbacks  
        # define GLUT callbacks as ruby 1.9 lambdas (could use Proc.new)
 
  @idle = ->{
    t = Get(GLUT::ELAPSED_TIME) / 1000.0
    @t0_idle = t unless t0_idle
    # 90 degrees per second
    @angle += 70.0 * (t - t0_idle)
    @t0_idle = t
    PostRedisplay()
   }
    
  @visible = -> vis {
    IdleFunc(vis == GLUT::VISIBLE ? @idle : nil)
  }

  @mouse = -> button, state, x, y {
    @mouse = state
    @x0, @y0 = x, y
  }
  
  @motion = -> x, y {
    if @mouse == GLUT::DOWN
      @view_roty += @x0 - x
      @view_rotx += @y0 - y
    end
    @x0, @y0 = x, y
  }
    
  # begin draw
  @draw = ->{
    Clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT);

    PushMatrix()
    Rotate(@view_rotx, 1.0, 0.0, 0.0)
    Rotate(@view_roty, 0.0, 1.0, 0.0)
    Rotate(@view_rotz, 0.0, 0.0, 1.0)

    PushMatrix()
    Translate(-3.0, -2.0, 0.0)
    Rotate(@angle, 0.0, 0.0, 1.0)
    CallList(@gear1)
    PopMatrix()

    PushMatrix()
    Translate(3.1, -2.0, 0.0)
    Rotate(-2.0 * @angle -9.0, 0.0, 0.0, 1.0)
    CallList(@gear2)
    PopMatrix()

    PushMatrix()
    Translate(-3.1, 4.2, 0.0)
    Rotate(-2.0 * @angle -25.0, 0.0, 0.0, 1.0)
    CallList(@gear3)
    PopMatrix()

    PopMatrix()

    SwapBuffers()

    @frames = 0 unless frames
    @t0 = 0 unless t0

    @frames += 1
    t = Get(GLUT::ELAPSED_TIME)
    if t - t0 >= 5000
      seconds = (t - t0) / 1000.0
      fps = frames / seconds
      puts sprintf("%d frames in %6.3f seconds = %6.3f FPS", frames, seconds, fps)
      @t0, @frames = t, 0
      autoexit.nil? ? nil : (t <= 999.0 * autoexit) ? nil : exit(0)
    end
  }
  
  # end draw
  
  # Change view angle, exit upon ESC
  @key = -> k, x, y {
    case k
      when ?z
        @view_rotz += 5.0
      when ?Z
        @view_rotz -= 5.0
      when ?\e 
        exit(0)
    end
    PostRedisplay()
  }

  # Change view angle
  @special = -> k, x, y{
    case k
      when GLUT::KEY_UP
        @view_rotx += 5.0
      when GLUT::KEY_DOWN
        @view_rotx -= 5.0
      when GLUT::KEY_LEFT
        @view_roty += 5.0
      when GLUT::KEY_RIGHT
        @view_roty -= 5.0
    end
    PostRedisplay()
  }
  
  @reshape = -> width, height{
    h = height.to_f / width
    Viewport(0, 0, width, height)
    MatrixMode(GL::PROJECTION)
    LoadIdentity()
    Frustum(-1.0, 1.0, -h, h, 5.0, 60.0)
    MatrixMode(GL::MODELVIEW)
    LoadIdentity()
    Translate(0.0, 0.0, -40.0)
  }  
end 

fred = Gears.new
#fred.SetWindowTitle( ###some cstring### ) # how?
fred.start




Unlike ruby-processing there is absolutely no problem resizing (on linux), indeed you can and should experiment with resizing the frame whilst the animation is running. Also the syntax is not too bad makes me think I should explore it a bit more!!!

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