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

Monday 24 May 2010

Towards Penrose Tiling Using ruby-processing context-free DSL


#########################
# rubystar.rb
#########################
load_library 'context_free'

PHI = (1 + Math.sqrt(5))/2

def setup_the_sunstar
  @hexa = ContextFree.define do
  ############ Begin defining custom terminals, as a sharp and flat triangles
    class << self
    include Math
      define_method(:sharp) do |some_options|      
        size, options = *self.get_shape_values(some_options)
        rot = options[:rotation]
        rotate(rot) if rot           # NB: sin(0) = 0, cos(0) = 1                                        
        super.triangle(0, 0, size, 0, size * cos(PI/5), size * sin(PI/5))
        rotate(-rot) if rot
      end
      define_method(:flat) do |some_options|      
        size, options = *self.get_shape_values(some_options)
        rot = options[:rotation]
        rotate(rot) if rot
        f = (options[:flip])? -1 : 1  # flip adjustment NB: sin(0) = 0, cos(0) = 1
        super.triangle(0, 0, size/PHI, 0, size * cos(PI * f/5), size * sin(PI * f/5))
        rotate(-rot) if rot
      end
    end
    ########### End definition of custom terminals 'sharp and flat'
    rule :tiling do    
      sun :brightness => 0.8
      star :brightness => 0.8, :alpha => 0.8
      tiling :size => 1/PHI, :brightness => 1.0
    end
  
    rule :dart do
      flat :size => 1, :color => [0.18, 0.6, 0.6]
      flat :size => 1, :color => [0.18, 1.0, 1.0], :flip => true
    end
  
    rule :kite do
      sharp :size => 1, :color => [0, 0.6, 0.6]
      sharp :size => 1, :color => [0, 1.0, 1.0], :rotation => 180, :flip => true
    end
  
    rule :star do    
      split do
        5.times do |i|
          dart :rotation => 72 * i
          rewind
        end
      end
    end
  
    rule :sun do
      split do
        5.times do |i|
          kite :rotation => 72 * i
          rewind
        end
      end
    end
  end
end

def setup
  size 800, 800
  background 150, 20, 0
  smooth
  setup_the_sunstar
  draw_it
end

def draw_it
  @hexa.render :tiling, :start_x => width/2, :start_y => height/2,
               :size => height
end




Sunday 23 May 2010

Ruby Processing Context Free DSL (how to do a cfdg PATH)

One way to create an equivalent of PATH (from C++ cfdg) is to use the processing shape facilities, here I have created a custom line-strip hexagon shape (ie no fill). But of course you could just as easily create a filled shape.


#########################
# hextube.rb
#########################
load_library 'context_free'

def setup_the_hextube
  @hexa = ContextFree.define do
    ############ Begin defining custom terminal, as a hexagon (path C++ cfdg)
    class << self    
      define_method(:hexagon) do |some_options|
        size, options = *self.get_shape_values(some_options)
        rot = (options[:rotation])? options[:rotation]: 0
        no_fill
        stroke(*options[:color])
        stroke_weight(size/30)
        begin_shape
        6.times do |i|
          vertex(size * Math.cos(Math::PI * i/3 + rot), size * Math.sin(Math::PI * i/3 + rot))
        end
        end_shape(CLOSE)
      end
    end
    ########### End definition of custom terminal 'hexagon'
    rule :hextube do
      hexa :brightness => 1.0
    end

    rule :hexa do
      hexagon :size => 1, :brightness => 1.0
      hexa :size => 0.9, :rotation => 5
    end
  end
end

def setup
  size 800, 800
  background 0
  smooth
  setup_the_hextube
  draw_it
end

def draw_it
  @hexa.render :hextube, :start_x => width/2, :start_y => height/2,
               :size => height/2.1, :color => [0, 1, 1, 0.5]
end




Monday 17 May 2010

Colored Penrose Tiling using LSystems

Colored Penrose Tiling uses my lsystem library see previous post for library/grammar.rb


#######################################################
# penrose tiling in ruby processing using LSystems
# in ruby-processing by Martin Prout
######################################################
require 'penrose_colored'

class Penrose < Processing::App
  load_libraries "grammar"

  attr_reader :penrose

  def setup
    size 800, 800
    stroke_weight 2
    smooth
    @penrose = PenroseColored.new
    penrose.create_grammar 5
    no_loop  
  end

  def draw
    background 0
    penrose.render
  end

end

#############################
# penrose_colored.rb
#############################

class PenroseColored
  include Processing::Proxy

  attr_reader :axiom, :grammar, :start_length, :theta, :production, :draw_length,
    :repeats, :xpos, :ypos
  
  XPOS = 0     # placeholders for pen array
  YPOS = 1
  ANGLE = 2
  COL = 3
  DELTA = PI/5 # radians or 36 degrees
  RED = 70<<24|200<<16|0<<8|0   # using bit operations to set color int
  BLUE = 70<<24|0<<16|0<<8|200

  def initialize
    @axiom = "[X]2+[X]2+[X]2+[X]2+[X]"    # Note use abbrev form in axiom and rule    
    @grammar = Grammar.new axiom          # here number equals number of repeats
    @grammar.add_rule "F", ""             # delete 'F'
    @grammar.add_rule "W", "YBF2+ZRF4-XBF[-YBF4-WRF]2+" # substitution rules
    @grammar.add_rule "X", "+YBF2-ZRF[3-WRF2-XBF]+"
    @grammar.add_rule "Y", "-WRF2+XBF[3+YBF2+ZRF]-"
    @grammar.add_rule "Z", "2-YBF4+WRF[+ZRF4+XBF]2-XBF"  
    @start_length = 1000.0
    @theta = 0
    @xpos = width/2
    @ypos = height/2
    @production = axiom
    @draw_length = start_length
  end

  ##############################################################################
  # Not strictly in the spirit of either processing in my render
  # function I have ignored the processing translate/rotate functions in favour
  # of the direct calculation of the new x and y positions, thus avoiding such
  # affine transformations.
  ##############################################################################

  def render()
    repeats = 1
    pen = [xpos, ypos, theta, :R]   # simple array for pen, symbol :R = red
    stack = []                      # simple array for stack
    production.scan(/./).each do |element|
      case element
      when 'F'
        pen = draw_line(pen, draw_length)
      when '+'
        pen[ANGLE] += DELTA * repeats
        repeats = 1
      when '-'
        pen[ANGLE] -= DELTA * repeats
        repeats = 1
      when '['
        stack.push(pen.dup)  # push a copy current pen to stack
      when ']'
        pen = stack.pop      # assign current pen to instance off the stack
      when 'R', 'B'        
        pen[COL] = element.to_sym  # set pen color as symbol
      when 'W', 'X', 'Y', 'Z'  
      when '1', '2', '3', '4'
        repeats = Integer(element)
      else puts "Character '#{element}' not in grammar"
      end
    end
  end

  #####################################################
  # create grammar from axiom and # rules (adjust scale)
  #####################################################

  def create_grammar(gen)
    @draw_length *= 0.5**gen
    @production = grammar.generate gen
  end

  private
  ####################################################################
  # draws line using current pen position, color and length parameters
  # returns a pen corresponding to the new position
  ###################################################################

  def draw_line(pen, length)
    stroke (pen[COL] == :R)? RED : BLUE
    new_xpos = pen[XPOS] - length * cos(pen[ANGLE])
    new_ypos = pen[YPOS] - length * sin(pen[ANGLE])
    line(pen[XPOS], pen[YPOS], new_xpos, new_ypos)    # draw line
    return [new_xpos, new_ypos, pen[ANGLE], pen[COL]] # return pen @ new pos
  end
end





Monday 10 May 2010

Op Art using Voronoi Cell, Ruby Processing


load_libraries 'mesh'         # http://www.leebyron.com/else/mesh/
import 'megamu.mesh.Voronoi'
require 'set'
attr_reader :voronoi, :points, :left, :right, :top, :bottom

def setup()
  size(1000, 1000)
  color_mode HSB, 1.0
  @points = Set.new
  @points.clear
  @left = MRect.new 10, height/2, 20, height, 20
  @right = MRect.new 990, height/2, 20, height, 20
  @top = MRect.new width/2, 10, width, 20, 20
  @bottom = MRect.new width/2, 990, width, 20, 20
  smooth
#  no_loop
end

def draw
  @points.clear
  shape = MPoly.new width/3, height/3, 8, 150, 4
  shape1 = MPoly.new width/2, height/2, 8, 120, 4
  shape2 = MPoly.new width*0.667, height*0.667, 8, 150, 4
  shape3 = MPoly.new width/3, height*0.667, 3, 150, 4
  shape4 = MPoly.new width*0.667, height/3, 3, 150, 4
  @points.merge(left.points)
  @points.merge(right.points)
  @points.merge(top.points)
  @points.merge(bottom.points)
  @points.merge(shape.points)
  @points.merge(shape1.points)
  @points.merge(shape2.points)
  @points.merge(shape3.points)
  @points.merge(shape4.points)
  voronoi = Voronoi.new((@points.to_a).to_java(Java::float[]))
  regions = voronoi.get_regions
  regions.each do |region|
    region_coordinates = region.get_coords
    fill(rand)
    region.draw(self)
  end
end

class MRect # border for now
        attr_reader :x, :y, :w, :h, :skip, :points     
  def initialize x, y, w, h, skip
    @x = x - w/2
    @y = y - h/2
    @w = w
    @h = h
    @skip = skip
    @points = calculate_points
  end
  def calculate_points
    mpoints = Array.new
    (w/skip).times do
      (h/skip).times do
        mpoints.push([rand*w + x, rand*h + y])
      end
    end
    return mpoints
  end         
end

class MPoly # shape
  attr_reader :x, :y, :sides, :size, :theta, :delta, :repeats, :len, :points           
  def initialize x, y, sides, size, density
    @delta = -PI/sides
    @x = x
    @y = y
    @sides = sides
    @size = size
    @theta = 2 * PI/sides
    @repeats = density
    @len = size/repeats
    @points = calculate_points
  end
  def calculate_points
    mpoints = Array.new
    repeats.times do |i|
      sides.times do |j|
        mpoint_x = x + (i + 1) * len * cos(j * theta + delta)
        mpoint_y = y - (i + 1) * len * sin(j * theta + delta)
        mpoints.push [mpoint_x, mpoint_y]
      end
    end
    return mpoints
  end
  # def rotate delta
    # @delta += delta
    # calculate_points    
  # end          
end




Saturday 8 May 2010

Voronoi Cells from the Coordinates of A Penrose Tiling

Here's an idea to create 'regular' voronoi cells from fractal dimensions. Well recently I was working on Penrose Tiling created using LSystems, so that's were I started.


#########################################################
# Voronoi cells centered on penrose tiling coordinates
# generated by a Lindenmayer System in ruby-processing
# by Martin Prout
#######################################################
require 'penrose_tiling'
require 'set'
load_libraries 'grammar', 'mesh'
import 'megamu.mesh.Voronoi'

attr_reader :regions

def setup
  size 300, 300
  stroke 255, 255, 0  # yellow mesh
  stroke_weight 3
  fill 255, 0, 0      # red regions
  smooth
  penrose = PenroseTiling.new
  penrose.create_grammar 4
  set = penrose.get_points
  voronoi = Voronoi.new((set.to_a).to_java(Java::float[]))
  @regions = voronoi.get_regions
  no_loop  
end

def draw
  background 0
  regions.each do |region|
    region_coordinates = region.get_coords            
  region.draw(self)
  end
end

#######################################
# penrose_tiling.rb
# #####################################

require 'set'

class PenroseTiling
  include Processing::Proxy

  attr_reader :axiom, :grammar, :start_length, :theta, :production, :draw_length,
    :repeats, :xpos, :ypos
  
  XPOS = 0     # placeholders for turtle array
  YPOS = 1
  ANGLE = 2
  DELTA = PI/5 # radians or 36 degrees

  def initialize
  
    @axiom = "[X]2+[X]2+[X]2+[X]2+[X]"        # Note use of abbreviated rule
    @grammar = Grammar.new axiom              # here number equals number of repeats
    @grammar.add_rule "F", ""  
    @grammar.add_rule "X", "+YF2-ZF[3-WF2-XF]+"
    @grammar.add_rule "Y", "-WF2+XF[3+YF2+ZF]-"
    @grammar.add_rule "Z", "2-YF4+WF[+ZF4+XF]2-XF"
    @grammar.add_rule "W", "YF2+ZF4-XF[-YF4-WF]2+"
    @start_length = 300.0
    @theta = 0
    @xpos = width/2
    @ypos = height/2
    @production = axiom
    @draw_length = start_length
  end

  ##############################################################################
  # Not strictly in the spirit of either processing in my get_points
  # function I have ignored the processing translate/rotate functions in favour
  # of the direct calculation of the new x and y positions, thus avoiding such
  # affine transformations. Returns a Set of unique [x, y] coordinates.
  ##############################################################################

  def get_points()
    points = Set.new
    repeats = 1
    turtle = [xpos, ypos, theta]    # simple array for turtle
    points.add([xpos.round, ypos.round])
    stack = []                      # simple array for stack
    production.scan(/./).each do |element|
      case element
      when 'F'
        turtle = next_point(turtle, draw_length)      
        points.add([turtle[XPOS].round, turtle[YPOS].round]) unless (turtle[XPOS] < 0) || (turtle[YPOS] < 0)
      when '+'
        turtle[ANGLE] += DELTA * repeats
        repeats = 1
      when '-'
        turtle[ANGLE] -= DELTA * repeats
        repeats = 1
      when '['
        stack.push(turtle.clone)  # push a copy current turtle to stack
      when ']'
        turtle = stack.pop        # assign current turtle a instance popped from the stack
      when 'W', 'X', 'Y', 'Z'  
      when '1', '2', '3', '4'
        repeats = Integer(element)
      else puts "Character '#{element}' not in grammar"
      end
    end
    return points
  end

  ##############################
  # create grammar from axiom and # rules (adjust scale)
  ##############################

  def create_grammar(gen)
    @draw_length *= 0.5**gen
    @production = grammar.generate gen
  end


  private
  ######################################################
  # uses current turtle and length parameters to calculate
  # a turtle corresponding to the new position
  ######################################################

  def next_point(turtle, length)
    new_xpos = turtle[XPOS] + length * cos(turtle[ANGLE])
    new_ypos = turtle[YPOS] + length * sin(turtle[ANGLE])
    return [new_xpos, new_ypos, turtle[ANGLE]]
  end
end


For the grammar library see my PenroseTiling post.

Random Voronoi Sketch

Here is a simple sketch, that flashes new random voronoi cells, with random fill.

load_libraries 'mesh'         # http://www.leebyron.com/else/mesh/
import 'megamu.mesh.Voronoi'
require 'set'
attr_reader :voronoi, :points

def setup()
  size(300, 300)
  color_mode HSB, 1.0
end

def draw
  points = Set.new
  10.times do
    10.times do    
      points.add([rand*300, rand*300])
    end
  end 
  voronoi = Voronoi.new((points.to_a).to_java(Java::float[]))
  regions = voronoi.get_regions
  regions.each do |region|
    region_coordinates = region.get_coords
    fill(rand, rand, rand)
    region.draw(self)
  end 
end


Thursday 6 May 2010

A Simple flower using ruby processing context free DSL


#########################################
# A Simple context free DSL flower
# flower.rb after leaf.cfdg by apoc
#########################################

load_library 'context_free'

def setup_the_flower
  @petal= ContextFree.define do  
   
    rule :flower do
      circle :size => 5.0, :hue => 0.1, :saturation => 1.0, :brightness => 1.0, :alpha => 0.6
      split do
        12.times do |i|
          petal :rotation => 30*i, :brightness => 1.0
          rewind
        end
      end
    end

    rule :petal do
      split do
        petal_border :y => -17.5, :rotation => -30
        rewind
        petal_border :y => -17.5, :flip => true, :rotation => 330
      end
    end
   
    rule :petal_border do
      circle :size => 0.75, :hue => 0.1, :saturation => 0.8, :brightness => 0.2
      petal_border :y => 0.1, :rotation => 0.17, :size => 0.995
    end

  end
end

def setup
  size 400, 400
  color_mode HSB, 1.0
  background 0.1, 0.2, 0.8
  smooth
  setup_the_flower
  draw_it
end

def draw_it
  @petal.render :flower, :start_x => width/2, :start_y => height/2,
               :size => height/40
end




Custom hbar shape for ruby processing Context Free DSL

Here I use a custom horizontal bar to mimic a electrophoresis gel (such as PCR). Also demonstrates the use of a low probability, empty rule to terminate recursion (see the third definition of the band rule).

#########################
# electrophoresis.rb
# demonstrates the hbar
# custom terminal
#########################
load_library 'context_free'

def setup_the_gel
  @pcr = ContextFree.define do
    ############ Begin defining custom terminal, a proportional horizontal bar
    class << self   
      define_method(:hbar) do |some_options|
        size, options = *self.get_shape_values(some_options)
        ht = some_options[:ht]|| 0.1    # default hbar width is 0.1
        ratio = ht * size
        rot = options[:rotation]
        rect_mode(CENTER)
        rotate(rot) if rot
        rect(-0.5 * size, -0.5 * ratio, 0.5 * size, 0.5 * ratio)
      end
    end
    ########### End definition of custom terminal 'hbar'
 
    rule :gel do
      dna :brightness => 1.0
    end
 
    rule :dna do
      split do
        26.times do
          band :x => 0.6
          rewind
        end
      end
    end
 
    rule :band do       # narrow band with 0.66' probability        
      hbar :size => 0.8, :ht => 0.1, :brightness => 0.3, :alpha => 0.3, :hue => 0.7, :saturation => 1.0
      band :brightness => 0.5
    end
 
    rule :band, 0.5 do   # double width band with 0.33' probability      
      hbar :size => 0.8, :ht => 0.15, :brightness => 0.8, :alpha => 0.6, :hue => -0.1, :saturation => 1.0
      band :brightness => 0.5
    end
 
    rule :band, 0.08 do  # a low probability empty rule used to end recursion    
    end 
   
   rule :band do            
     band :y => -0.23
   end
 
    rule :band do         
      band :y => 0.17
    end
 
    rule :band do
       band :y => 0.29 
    end

   rule :band do
      band :y => -0.33 
   end

  end
end

def setup
  size 500, 300
  background 0, 0, 180
  smooth
  setup_the_gel
  draw_it
end

def draw_it
  @pcr.render :gel, :start_x => -50, :start_y => height/2,
               :size => height/5, :color => [0.7, 0.8, 0.8, 1.0]
end


Wednesday 5 May 2010

Penrose Tiling Ruby-Processing LSystems


######################################################
# A Lindenmayer System in ruby-processing by Martin Prout
# Loosely based on a processing PenroseTiling sketch
# by Geraldine Sarmiento
# tiling.rb
######################################################
require 'penrose_tiling'

class Penrose < Processing::App
  load_libraries "grammar"

  attr_reader :penrose

  def setup
    size 1000, 1000
    stroke 255, 60
    stroke_weight 2
    smooth
    @penrose = PenroseTiling.new
    penrose.create_grammar 4
    no_loop
  end

  def draw
    background 0
    penrose.render
  end
end

########################################
# penrose_tiling.rb
########################################

class PenroseTiling
  include Processing::Proxy

  attr_reader :axiom, :grammar, :start_length, :theta, :production, :draw_length,
    :repeats, :xpos, :ypos

  XPOS = 0     # placeholders for turtle array
  YPOS = 1
  ANGLE = 2
  DELTA = PI/5 # radians or 36 degrees

  def initialize
    @axiom = "[X]2+[X]2+[X]2+[X]2+[X]"        # Note use of abbreviated rule
    @grammar = Grammar.new axiom              # here number equals number of repeats
    @grammar.add_rule "F", ""
    @grammar.add_rule "X", "+YF2-ZF[3-WF2-XF]+"
    @grammar.add_rule "Y", "-WF2+XF[3+YF2+ZF]-"
    @grammar.add_rule "Z", "2-YF4+WF[+ZF4+XF]2-XF"
    @grammar.add_rule "W", "YF2+ZF4-XF[-YF4-WF]2+"
    @start_length = 1000.0
    @theta = 0
    @xpos = width/2
    @ypos = height/2
    @production = axiom
    @draw_length = start_length
  end

  ##############################################################################
  # Not strictly in the spirit of either processing in my render
  # function I have ignored the processing translate/rotate functions in favour
  # of the direct calculation of the new x and y positions, thus avoiding such
  # affine transformations.
  ##############################################################################

  def render()
    repeats = 1
    turtle = [xpos, ypos, theta]    # a simple array for turtle
    stack = []                      # a simple array for stack
    production.scan(/./).each do |element|
      case element
      when 'F'
        turtle = draw_line(turtle, draw_length)
      when '+'
        turtle[ANGLE] += DELTA * repeats
        repeats = 1
      when '-'
        turtle[ANGLE] -= DELTA * repeats
        repeats = 1
      when '['
        stack.push(turtle.dup)  # push a copy current turtle to stack
      when ']'
        turtle = stack.pop        # assign current turtle a instance popped from the stack
      when 'W', 'X', 'Y', 'Z'
      when '2', '3', '4'
        repeats = Integer(element)
      else puts "Character '#{element}' not in grammar"
      end
    end
  end

  ##############################
  # create grammar from axiom and # rules (adjust scale)
  ##############################

  def create_grammar(gen)
    @draw_length *= 0.5**gen
    @production = grammar.generate gen
  end

  private
  ######################################################
  # draws line using current turtle and length parameters
  # returns a turtle corresponding to the new position
  ######################################################

  def draw_line(turtle, length)
    new_xpos = turtle[XPOS] - length * cos(turtle[ANGLE])
    new_ypos = turtle[YPOS] - length * sin(turtle[ANGLE])
    line(turtle[XPOS], turtle[YPOS], new_xpos, new_ypos)
    return [new_xpos, new_ypos, turtle[ANGLE]]
  end
end

#############################################################
# library/grammar.rb
# Non-stochastic grammar
# with unique premise/rules
############################################################
class Grammar
  attr_accessor :axiom, :rules

  def initialize axiom
    @axiom = axiom
    @rules = Hash.new
  end

  def add_rule premise, rule
    rules.store(premise, rule)
  end

  ##########################################
  # replace each pre char with a unique rule
  ##########################################
  def new_production production
    production.gsub!(/./) { |c| (r = @rules[c]) ? r : c }
  end

  ##########################################
  # control the number of iterations
  # default 0, returns the axiom
  ##########################################
  def generate repeat = 0
    prod = axiom
    repeat.times do
      prod = new_production prod
    end
    return prod
  end
end



  

Followers

Blog Archive

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2