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

Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Thursday, 2 July 2015

Experimenting with standalone app using jruby

On the jruby wiki there is an explanation of how to create standalone JRuby apps, it appears to be v. old referring to since JRuby-1.6 but I have found it still works with JRuby-9.0.0.0-SNAPSHOT (as of 2 July 2015) see steps I took:-
cp jruby/lib/jruby.jar myapp.jar         # adjust path to jruby.jar
echo "puts 'hello'" >> jar-bootstrap.rb
jar ufe myapp.jar org.jruby.JarBootstrapMain jar-bootstrap.rb
java -jar myapp.jar

output was as expected 'hello'. Perhaps I could harness this for JRubyArt app export?
Here is what my myapp.jar MANIFEST.MF looks like:-
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: tux
Created-By: Apache Maven 3.3.3
Build-Jdk: 1.8.0_45
Main-Class: org.jruby.JarBootstrapMain

Apparently you can also embed additional .rb files into the jar, and the root of the jar file will be used as an implicit LOAD_PATH entry. Warbler is another option to consider.

Sunday, 28 September 2014

Running ribiprocessing from netbeans

Above is a screenshot of me running seeking_neural.rb from Netbeans using ribiprocessing,a really lighweight wrapper for processing. To do this all I needed was the netbeans ruby plugin and then to install (to netbeans) the local gem ribiprocessing-0.1.0.gem.

Tuesday, 24 June 2014

Ruby 2D games using ruby-processing

Morohoshi-san is developing a ruby games application based on dxruby_sdl here is the tweet from ruby-gems:-




This is a sketch from dxruby_sdl on github run on my linux box (better if I had game controllers I am sure).

Here is the project on github.

Tuesday, 29 January 2013

Adding requirements rspec

In my previous post I presented an initial specification for my lsystems csgrammar parser. That is the most basic form, in which context is given by the preceding or following token in the axiom/production. Here is the specification of a more complicated parser that can take an ignore string, that includes those tokens that should be ignored wrt context. Typically the tokens will represent special instructions to interpreter such as [ and ] which mean push/pop matrix etc.
require 'CSLSystem'

IGNORE = 'ignore'

describe "LSystem" do
  context "given axiom 'biaaaaaa'" do
    context "with function a=b if context, ignoring IGNORE" do
      describe "#generate" do
        it "should equal 'biaaaaaa' at zero iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(0).should == 'biaaaaaa'
        end
        it "should equal 'aibaaaaa' after one iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(1).should == 'aibaaaaa'
        end
        it "should equal 'aiabaaaa' after one iteration" do
          LSystem.new('aibaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(1).should == 'aiabaaaa'
        end
        it "should equal 'aiabaaaa' after two iterations" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(2).should == 'aiabaaaa'
        end
        it "should equal 'aiaabaaa' after three iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(3).should == 'aiaabaaa'
        end
        it "should equal 'aiaaaaba' after five iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(5).should == 'aiaaaaba'
        end
        it "should equal 'biaaaaaa' after seven iterations" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(7).should == 'biaaaaaa'
        end
      end
    end
  end
end


Here is the revised grammar parser:-
######################################
# cs_grammar.rb a context sensitive
# 1-L lsystem grammar for 
# ruby/ruby-processing
# by Martin Prout (January 2013)
######################################



##################################
# The grammar class stores rules
# in two Hashes, one for cs rules,
# one for context free rules. Rules
# are filtered on input, and context
# is checked using get_rule in production
##################################

class LSystem

  attr_reader :axiom, :context, :no_context, :idx, :ignore
  def initialize(axiom, rules, ignore)
    @axiom = axiom
    @no_context = {}
    @context = {}
    @ignore = ignore
    rules.each_pair do |pair|
      add_rule pair[0], pair[1]
    end
  end

  def add_rule(pre, rule)
    if pre.length == 3
      if pre[1] == '<'
        @context[pre[2]] = pre
      elsif pre[1] == '>'
        @context[pre[0]] = pre
      end
      @no_context[pre] = rule # key length == 3
    elsif pre.length == 1
      @no_context[pre] = rule # key length == 1
    else
      print "unrecognized grammar '#{pre}'"
    end
  end

  def generate(repeat = 0) # repeat iteration grammar rules
    prod = axiom
    repeat.times do
      prod = new_production(prod)
    end
    return prod
  end


  def new_production prod  # single iteration grammar rules
    @idx = -1
    prod.gsub!(/./) do |ch|
      get_rule(prod, ch)
    end
  end

  def get_rule prod, ch
    rule = ch # default is return original character as rule (no change)
    @idx += 1 # increment the index of axiom/production as a side effect
    if (context.has_key?(ch))
      if context[ch][1] == '<'
        cs_char = context[ch][0]
        rule = no_context[context[ch]] if cs_char == get_context(prod, idx, -1) # use context sensitive rule
      elsif context[ch][1] == '>'
        cs_char = context[ch][2]
        rule = no_context[context[ch]] if cs_char == get_context(prod, idx, 1) # use context sensitive rule
      end
    else
      rule = no_context[ch] if no_context.has_key?(ch) # context free rule if it exists
    end
    return rule
  end

  def get_context prod, idx, inc
    index = idx + inc
    while (ignore.include? prod[index])
      index += inc
    end
    return prod[index]
  end
end

Monday, 28 January 2013

Behaviour driven development rspec

Here is an initial specification for a context sensitive LSystem in ruby (example taken from abop, function should really be a=b if context):-
require 'CSLSystem'

describe "LSystem" do
  context "given axiom 'baaaaaa'" do
    context "with function b=a" do
      describe "#generate" do
        it "should equal 'baaaaaa' at zero iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(0).should == 'baaaaaa'
        end
        it "should equal 'abaaaaa' after one iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(1).should == 'abaaaaa'
        end
        it "should equal 'aabaaaa' after one iteration" do
          LSystem.new('abaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(1).should == 'aabaaaa'
        end
        it "should equal 'aabaaaa' after two iterations" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(2).should == 'aabaaaa'
        end
        it "should equal 'aaabaaa' after three iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(3).should == 'aaabaaa'
        end
        it "should equal 'aaaaaba' after five iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(5).should == 'aaaaaba'
        end
        it "should equal 'baaaaaa' after seven iterations" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(7).should == 'baaaaaa'
        end
      end
    end
  end
end


Here is some code designed to pass the rspec tests:-
######################################
# cs_grammar.rb a context sensitive
# 1-L lsystem grammar for 
# ruby/ruby-processing
# by Martin Prout (January 2013)
######################################



##################################
# The grammar class stores rules
# in two Hashes, one for cs rules,
# one for context free rules. Rules
# are filtered on input, and context
# is checked using get_rule in production
##################################

class LSystem

  attr_reader :axiom, :context, :no_context, :idx
  def initialize(axiom, rules)
    @axiom = axiom
    @no_context = {}
    @context = {}
    rules.each_pair do |pair|
      add_rule pair[0], pair[1]
    end
  end

  def add_rule(pre, rule)
    if pre.length == 3
      if pre[1] == '<'
        @context[pre[2]] = pre
      elsif pre[1] == '>'
        @context[pre[0]] = pre if pre[1] == '>'
      end
      @no_context[pre] = rule # key length == 3
    elsif pre.length == 1
      @no_context[pre] = rule # key length == 1
    else
      print "unrecognized grammar '#{pre}'"
    end
  end

  def generate(repeat = 0) # repeat iteration grammar rules
    prod = axiom
    repeat.times do
      prod = new_production(prod)
    end
    return prod
  end


  def new_production prod  # single iteration grammar rules
    @idx = 0
    prod.gsub!(/./) do |ch|
      get_rule(prod, ch)
    end
  end

  def get_rule prod, ch
    rule = ch # default is return original character as rule (no change)
    @idx += 1 # increment the index of axiom/production as a side effect
    if (context.has_key?(ch))
      if context[ch][1] == "<"
        cs_char = context[ch][0]
        rule = no_context[context[ch]] if cs_char == prod[idx - 2] # use context sensitive rule
      elsif context[ch][1] == ">"
        cs_char = context[ch][2]
        rule = no_context[context[ch]] if cs_char == prod[idx] # use context sensitive rule
      end
    else
      rule = no_context[ch] if no_context.has_key?(ch) # context free rule if it exists
    end
    return rule
  end
end

Result 
.......


Finished in 0.00261 seconds 7 examples, 0 failures

Some more tests...
require 'CSLSystem'

describe "LSystem" do
  context "given axiom 'aaaaaaab'" do
    context "with function b=a" do
      describe "#generate" do
        it "should equal 'aaaaaaab' at zero iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(0).should == 'aaaaaaab'
        end
        it "should equal 'aaaaaba' after one iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(1).should == 'aaaaaaba'
        end
        it "should equal 'aaaabaa' after one iteration" do
          LSystem.new('aaaaaaba', {'b' => 'a', 'a>b' => 'b'}).generate(1).should == 'aaaaabaa'
        end
        it "should equal ''aaaabaa'' after two iterations" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(2).should == 'aaaaabaa'
        end
        it "should equal 'aaabaaa' after three iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(3).should == 'aaaabaaa'
        end
        it "should equal 'abaaaaa' after five iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(5).should == 'aabaaaaa'
        end
        it "should equal 'aaaaaaab' after seven iterations" do
          LSystem.new('aaaaaaaa', {'b' => 'a', 'a>b' => 'b'}).generate(7).should == 'aaaaaaaa'
        end
      end
    end
  end
end


Here is the revised code to pass both sets of test...
######################################
# cs_grammar.rb a context sensitive
# 1-L lsystem grammar for 
# ruby/ruby-processing
# by Martin Prout (January 2013)
######################################



##################################
# The grammar class stores rules
# in two Hashes, one for cs rules,
# one for context free rules. Rules
# are filtered on input, and context
# is checked using get_rule in production
##################################

class LSystem

  attr_reader :axiom, :context, :no_context, :idx
  def initialize(axiom, rules)
    @axiom = axiom
    @no_context = {}
    @context = {}
    rules.each_pair do |pair|
      add_rule pair[0], pair[1]
    end
  end

  def add_rule(pre, rule)
    if pre.length == 3
      if pre[1] == '<'
        @context[pre[2]] = pre
      elsif pre[1] == '>'
        @context[pre[0]] = pre 
      end
      @no_context[pre] = rule # key length == 3
    elsif pre.length == 1
      @no_context[pre] = rule # key length == 1
    else
      print "unrecognized grammar '#{pre}'"
    end
  end

  def generate(repeat = 0) # repeat iteration grammar rules
    prod = axiom
    repeat.times do
      prod = new_production(prod)
    end
    return prod
  end


  def new_production prod  # single iteration grammar rules
    @idx = -1
    prod.gsub!(/./) do |ch|
      get_rule(prod, ch)
    end
  end

  def get_rule prod, ch
    rule = ch # default is return original character as rule (no change)
    @idx += 1 # increment the index of axiom/production as a side effect
    if (context.has_key?(ch))
      if context[ch][1] == '<'
        cs_char = context[ch][0]
        rule = no_context[context[ch]] if cs_char == prod[idx - 1] # use context sensitive rule
      elsif context[ch][1] == '>'
        cs_char = context[ch][2]
        rule = no_context[context[ch]] if cs_char == prod[idx + 1] # use context sensitive rule
      end
    else
      rule = no_context[ch] if no_context.has_key?(ch) # context free rule if it exists
    end
    return rule
  end
end

Tuesday, 1 March 2011

Exploring Context Sensitive LSystem rules in ruby / ruby-processing

Warning the following code depends on ruby 1.9, where string index returns a char rather than an ascii value (ruby 1.8) see following post for how this tripped me up at first when exploring a proper ruby-processing example. (Update 5 March 2011)


######################################
# cs_grammar.rb a context sensitive
# 1-L lsystem grammar for
# ruby/ruby-processing
# by Martin Prout (1 March 2011)
######################################

#######################
# A helper class stores
# cs rules idx, and cchar
# methods extract cs data
#######################
class CSRule
  
  attr_accessor :pre, :crule
  
  def initialize pre, crule
    @pre, @crule = pre, crule
  end
  
  def idx
    x = 0
    if (pre[1] == "<")
      x -= 2
    end
    return x
  end
  
  def cchar
    return pre[0]
  end

end

##################################
# The grammar class stores rules
# in two Hashes, one for cs rules,
# one for context free rules. Rules
# are filtered on input, and context
# is checked using get_rule in production
##################################

class CSGrammar

  attr_reader :axiom, :context, :no_context, :idx
  def initialize(axiom)
    @axiom = axiom
    @no_context = Hash.new
    @context = Hash.new
  end
  
  def add_rule(pre, rule)
    case pre.length
      when 3
        @context.store(pre[2], CSRule.new(pre, rule)) # index, context, rule
      when 1
        @no_context.store pre, rule
      else print "unrecognized grammar '#{pre}'"  
    end
  end
  
  def generate(repeat = 0) # repeat iteration grammar rules
    prod = axiom
    repeat.times do
      prod = new_production(prod)
    end
    return prod
  end 
  
  
  def new_production prod  # single iteration grammar rules
    @idx = 0
    prod.gsub!(/./) do |ch|
      get_rule(prod, ch)
    end
  end
  
  def get_rule prod, ch
    rule = ch # default is return original character as rule (no change)
    @idx += 1 # increment the index of axiom/production as a side effect
    if (context.has_key?(ch)) && (prod[context[ch].idx + idx] == context[ch].cchar)
      rule = context[ch].crule  # use context sensitive rule
    else
      rule = no_context[ch] if no_context.has_key?(ch) # context free rule if it exists
    end
    return rule
  end
end

# Test data taken from ABOP

7.times do |i|
   grammar = CSGrammar.new("baaaaaa")
   grammar.add_rule("b<a", "b")  # context sensitive rule replace a when preceded by b
   grammar.add_rule("b", "a")
   result = grammar.generate(i)
   print result << "\n"
end



Test Output:

baaaaaa
abaaaaa
aabaaaa
aaabaaa
aaaabaa
aaaaaba
aaaaaab

Note the way b 'travels' from left to right through the production string in the test output.

Reference:

The Algorithmic Beauty of Plants

Przemyslaw Prusinkiewicz
Aristid Lindenmayer

Saturday, 24 July 2010

Ruby Structs and floats required in place of Array in ruby-opengl

I can't think that I'm the only person that has encountered this, but neither have I found any references to the issue (or clues in the distribution).
I have recently been playing with ruby1.9-opengl bindings, and I have found the following issues:-

1. Ruby Strings are no longer accepted (to give window title), my solution is to replace String with an empty array anything else (including no entry) seems to be interpreted as a String (input needs to be a CString, ie an array of C chars or possibly pointer thereof).

2. Properties such as color and position, in the Red Book and other examples (included with the source download) are given as an Array. When I've tried to run these examples, I get error messages that a Struct is required not an Array (see below for my coding of Struct to replace Array the name of the Struct and symbols chosen for the Struct variable is unimportant). Parameter Arrays with single float item seem to require a float replacement.


#
# Copyright (c) Mark J. Kilgard, 1994.
#
# (c) Copyright 1993, Silicon Graphics, Inc.
# ALL RIGHTS RESERVED
# Permission to use, copy, modify, and distribute this software for
# any purpose and without fee is hereby granted, provided that the above
# copyright notice appear in all copies and that both the copyright notice
# and this permission notice appear in supporting documentation, and that
# the name of Silicon Graphics, Inc. not be used in advertising
# or publicity pertaining to distribution of the software without specific,
# written prior permission.
#
# THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
# AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
# INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
# FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
# GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
# SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
# KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
# LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
# THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
# ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
# POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
#
# US Government Users Restricted Rights
# Use, duplication, or disclosure by the Government is subject to
# restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
# (c)(1)(ii) of the Rights in Technical Data and Computer Software
# clause at DFARS 252.227-7013 and/or in similar or successor
# clauses in the FAR or the DOD or NASA FAR Supplement.
# Unpublished-- rights reserved under the copyright laws of the
# United States.  Contractor/manufacturer is Silicon Graphics,
# Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
#
# OpenGL(TM) is a trademark of Silicon Graphics, Inc.
#
# material.c
# This program demonstrates the use of the GL lighting model.
# Several objects are drawn using different material characteristics.
# A single light source illuminates the objects.
require 'opengl'
include Gl,Glu,Glut

Properties = Struct.new("Properties", :a, :b, :c, :d)

# Initialize z-buffer, projection matrix, light source,
# and lighting model.  Do not specify a material property here.
def myinit
        ambient = Properties.new(0.0, 0.0, 0.0, 1.0 )
        diffuse = Properties.new(1.0, 1.0, 1.0, 1.0)
        position = Properties.new(0.0, 3.0, 2.0, 0.0)
        lmodel_ambient = Properties.new(0.4, 0.4, 0.4, 1.0)
        local_view = 0.0
  
        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_LESS)
  
        glLight(GL_LIGHT0, GL_AMBIENT, ambient)
        glLight(GL_LIGHT0, GL_DIFFUSE, diffuse)
        glLight(GL_LIGHT0, GL_POSITION, position)
        glLightModel(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient)
        glLightModel(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view)
  
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
  
        glClearColor(0.0, 0.1, 0.1, 0.0)
end

# Draw twelve spheres in 3 rows with 4 columns.  
# The spheres in the first row have materials with no ambient reflection.
# The second row has materials with significant ambient reflection.
# The third row has materials with colored ambient reflection.
#
# The first column has materials with blue, diffuse reflection only.
# The second column has blue diffuse reflection, as well as specular
# reflection with a low shininess exponent.
# The third column has blue diffuse reflection, as well as specular
# reflection with a high shininess exponent (a more concentrated highlight).
# The fourth column has materials which also include an emissive component.
#
# glTranslatef() is used to move spheres to their appropriate locations.
display = proc do
        no_mat = Properties.new( 0.0, 0.0, 0.0, 1.0)
        mat_ambient = Properties.new(0.7, 0.7, 0.7, 1.0)
        mat_ambient_color = Properties.new(0.8, 0.8, 0.2, 1.0)
        mat_diffuse = Properties.new(0.1, 0.5, 0.8, 1.0)
        mat_specular = Properties.new(1.0, 1.0, 1.0, 1.0)
        no_shininess = 0.0
        low_shininess = 5.0
        high_shininess = 100.0
        mat_emission = Properties.new(0.3, 0.2, 0.2, 0.0)
  
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  
        # draw sphere in first row, first column
        # diffuse reflection only no ambient or specular  
        glPushMatrix()
        glTranslate(-3.75, 3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, no_mat)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, no_mat)
        glMaterial(GL_FRONT, GL_SHININESS, no_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in first row, second column
        # diffuse and specular reflection low shininess no ambient
        glPushMatrix()
        glTranslate(-1.25, 3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, no_mat)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, mat_specular)
        glMaterial(GL_FRONT, GL_SHININESS, low_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in first row, third column
        # diffuse and specular reflection high shininess no ambient
        glPushMatrix()
        glTranslate(1.25, 3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, no_mat)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, mat_specular)
        glMaterial(GL_FRONT, GL_SHININESS, high_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in first row, fourth column
        # diffuse reflection emission no ambient or specular reflection
        glPushMatrix()
        glTranslate(3.75, 3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, no_mat)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, no_mat)
        glMaterial(GL_FRONT, GL_SHININESS, no_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, mat_emission)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in second row, first column
        # ambient and diffuse reflection no specular  
        glPushMatrix()
        glTranslate(-3.75, 0.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, no_mat)
        glMaterial(GL_FRONT, GL_SHININESS, no_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in second row, second column
        # ambient, diffuse and specular reflection low shininess
        glPushMatrix()
        glTranslate(-1.25, 0.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, mat_specular)
        glMaterial(GL_FRONT, GL_SHININESS, low_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in second row, third column
        # ambient, diffuse and specular reflection high shininess
        glPushMatrix()
        glTranslate(1.25, 0.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, mat_specular)
        glMaterial(GL_FRONT, GL_SHININESS, high_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in second row, fourth column
        # ambient and diffuse reflection emission no specular
        glPushMatrix()
        glTranslate(3.75, 0.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, no_mat)
        glMaterial(GL_FRONT, GL_SHININESS, no_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, mat_emission)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in third row, first column
        # colored ambient and diffuse reflection no specular  
        glPushMatrix()
        glTranslate(-3.75, -3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient_color)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, no_mat)
        glMaterial(GL_FRONT, GL_SHININESS, no_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in third row, second column
        # colored ambient, diffuse and specular reflection low shininess
        glPushMatrix()
        glTranslate(-1.25, -3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient_color)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, mat_specular)
        glMaterial(GL_FRONT, GL_SHININESS, low_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in third row, third column
        # colored ambient, diffuse and specular reflection high shininess
        glPushMatrix()
        glTranslate(1.25, -3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient_color)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, mat_specular)
        glMaterial(GL_FRONT, GL_SHININESS, high_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, no_mat)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        # draw sphere in third row, fourth column
        # colored ambient and diffuse reflection emission no specular
        glPushMatrix()
        glTranslate(3.75, -3.0, 0.0)
        glMaterial(GL_FRONT, GL_AMBIENT, mat_ambient_color)
        glMaterial(GL_FRONT, GL_DIFFUSE, mat_diffuse)
        glMaterial(GL_FRONT, GL_SPECULAR, no_mat)
        glMaterial(GL_FRONT, GL_SHININESS, no_shininess)
        glMaterial(GL_FRONT, GL_EMISSION, mat_emission)
        glutSolidSphere(1.0, 16, 16)
        glPopMatrix()
  
        glutSwapBuffers()
end

myReshape = proc do |w, h|
        glViewport(0, 0, w, h)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        if (w <= (h * 2))
                glOrtho(-6.0, 6.0, -3.0*(h.to_f*2)/w,   3.0*(h.to_f*2)/w, -10.0, 10.0)
        else
                glOrtho(-6.0*w.to_f/(h*2), 6.0*w.to_f/(h*2), -3.0, 3.0, -10.0, 10.0)
        end
        glMatrixMode(GL_MODELVIEW)
end

keyboard = Proc.new do |key, x, y|
        case (key)
                when ?\e
                exit(0);
        end
end

# Main Loop
# Open window with initial window size, title bar,
# RGBA display mode, and handle input events.
glutInit()
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(600, 600)
glutInitWindowPosition(100, 100)
glutCreateWindow([])
myinit()
glutReshapeFunc(myReshape)
glutDisplayFunc(display)
glutKeyboardFunc(keyboard)
glutMainLoop()





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