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

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.

No comments:

Post a Comment

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