Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
Showing posts with label l-system fractal. Show all posts
Showing posts with label l-system fractal. Show all posts
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
Labels:
l-system fractal,
penrose tiling,
ruby processing
Monday, 8 March 2010
Towards Post Production Transformation of LSystem Fractals
I've finally got round to reading "Fractals Everywhere" by Michael F. Barnsley. The math is not overly complicated just a bit unfamiliar. Anyway I'm sort of inspired by some the transforms in the book, to see what I could come up with. I'm particularly attracted by the idea of projecting a 2D fractal into a 3D dimensional space. There are possibly too many different ways of do this (including paper modelling, something I'd not considered before I saw it today over on the processing discourse). My first approach will be to produce a data set for a regular 2D fractal, and thereafter apply some rules to create a deformation of the pattern. To this end I've re-worked my snake-kolam to create a data set (an array of points). The first transformation rules I have created (see ScalingTool class) merely scale and center the fractal. Here is the code:-
##
# Lindenmayer System in ruby-processing by Martin Prout
###
class Kolam_Test < Processing::App
load_libraries 'kolam'
attr_reader :kolam
def setup
size 500, 500
@kolam = Kolam.new
kolam.create_grammar 3 # create grammar from rules
kolam.translate # translate grammar to points
no_loop
end
def draw
background 0
kolam.render width, height # adjust points to fit frame & render
end
end
############################
# library/kolam/kolam.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
############################
# snake kolam using l-systems
############################
BORDER = 10 # global border constant
XPOS = 0 # global point array constants
YPOS = 1
class Kolam
include Processing::Proxy
attr_accessor :axiom, :xpos, :ypos, :grammar, :production, :draw_length, :points
ANGLE = 2
DELTA = (Math::PI/180) * 90.0 # convert degrees to radians using ruby
def initialize
@axiom = "FX+F+FX+F"
@grammar = Grammar.new(axiom)
grammar.add_rule("X", "X-F-F+FX+F+FX-F-F+FX")
@theta = DELTA
@points = [[0, 0]] # initialize points array with first point
@draw_length = 1.0
@production = axiom
@xpos = 0
@ypos = 0
end
def translate # NB not using processing affine transforms here
turtle = [xpos, ypos, 0.0]
production.scan(/./).each do |element|
case element
when 'F'
turtle = store_line(turtle, draw_length)
when '+'
turtle[ANGLE] += DELTA
when '-'
turtle[ANGLE] -= DELTA
when 'X' # do nothing except recognize 'X' as a word in the L-system grammar
else
puts "Character '#{element}' is not in grammar"
end
end
end
def render(width, height)
st = ScalingTool.new width, height, points
data = st.scale_to_fit
no_fill
stroke(0, 255, 0)
stroke_width(2)
begin_shape
data.each do |point|
vertex(point[XPOS], point[YPOS])
end
end_shape
end
##############################
# create grammar from axiom and rules
# leave scaling & postioning to render
##############################
def create_grammar(gen)
@production = @grammar.generate gen
end
private
######################################################
# calculate and store line using current turtle and length parameters
# returns a turtle corresponding to the new position
######################################################
def store_line(turtle, length)
new_xpos = turtle[XPOS] + length * Math.cos(turtle[ANGLE])
new_ypos = turtle[YPOS] + length * Math.sin(turtle[ANGLE])
*point = new_xpos, new_ypos # collect coordinates
@points.push(point)
*turtle = new_xpos, new_ypos, turtle[ANGLE] % (Math::PI * 2) # collect coordinates & angle
end
end
###################################
# scaling tool, scale and center fractal
###################################
class ScalingTool
attr_reader :max_height, :max_width, :lowest_y, :lowest_x, :highest_y, :highest_x, :raw_data, :scale
def initialize max_width, max_height, data = []
@max_width = max_width - BORDER
@max_height = max_height - BORDER
@raw_data = data
@lowest_x = 0
@lowest_y = 0
@highest_x = 0
@highest_y = 0
@scale = 1
end
def scale_to_fit
processed = raw_data
scale = calculate_scale_factor
processed.each do |item|
item[XPOS] = scale * (item[XPOS] - lowest_x) + BORDER/2
item[YPOS] = scale * (item[YPOS] - lowest_y) + BORDER/2
end
return processed
end
private
def calculate_x_range
@raw_data.each do |item|
@lowest_x = item[XPOS] unless lowest_x < item[XPOS]
@highest_x = item[XPOS] unless highest_x > item[XPOS]
end
highest_x - lowest_x
end
def calculate_y_range
@raw_data.each do |item|
@lowest_y = item[YPOS] unless lowest_y < item[YPOS]
@highest_y = item[YPOS] unless highest_y > item[YPOS]
end
highest_y - lowest_y
end
#################################################
# Returns the smallest of width or height factors
# as side effect stores lowest x and y values
#################################################
def calculate_scale_factor
scale_x = (max_height * 1.0)/calculate_x_range
scale_y = (max_width * 1.0)/calculate_y_range
(scale_x < scale_y) ? scale_x : scale_y
end
end
##
# Lindenmayer System in ruby-processing by Martin Prout
###
class Kolam_Test < Processing::App
load_libraries 'kolam'
attr_reader :kolam
def setup
size 500, 500
@kolam = Kolam.new
kolam.create_grammar 3 # create grammar from rules
kolam.translate # translate grammar to points
no_loop
end
def draw
background 0
kolam.render width, height # adjust points to fit frame & render
end
end
############################
# library/kolam/kolam.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
############################
# snake kolam using l-systems
############################
BORDER = 10 # global border constant
XPOS = 0 # global point array constants
YPOS = 1
class Kolam
include Processing::Proxy
attr_accessor :axiom, :xpos, :ypos, :grammar, :production, :draw_length, :points
ANGLE = 2
DELTA = (Math::PI/180) * 90.0 # convert degrees to radians using ruby
def initialize
@axiom = "FX+F+FX+F"
@grammar = Grammar.new(axiom)
grammar.add_rule("X", "X-F-F+FX+F+FX-F-F+FX")
@theta = DELTA
@points = [[0, 0]] # initialize points array with first point
@draw_length = 1.0
@production = axiom
@xpos = 0
@ypos = 0
end
def translate # NB not using processing affine transforms here
turtle = [xpos, ypos, 0.0]
production.scan(/./).each do |element|
case element
when 'F'
turtle = store_line(turtle, draw_length)
when '+'
turtle[ANGLE] += DELTA
when '-'
turtle[ANGLE] -= DELTA
when 'X' # do nothing except recognize 'X' as a word in the L-system grammar
else
puts "Character '#{element}' is not in grammar"
end
end
end
def render(width, height)
st = ScalingTool.new width, height, points
data = st.scale_to_fit
no_fill
stroke(0, 255, 0)
stroke_width(2)
begin_shape
data.each do |point|
vertex(point[XPOS], point[YPOS])
end
end_shape
end
##############################
# create grammar from axiom and rules
# leave scaling & postioning to render
##############################
def create_grammar(gen)
@production = @grammar.generate gen
end
private
######################################################
# calculate and store line using current turtle and length parameters
# returns a turtle corresponding to the new position
######################################################
def store_line(turtle, length)
new_xpos = turtle[XPOS] + length * Math.cos(turtle[ANGLE])
new_ypos = turtle[YPOS] + length * Math.sin(turtle[ANGLE])
*point = new_xpos, new_ypos # collect coordinates
@points.push(point)
*turtle = new_xpos, new_ypos, turtle[ANGLE] % (Math::PI * 2) # collect coordinates & angle
end
end
###################################
# scaling tool, scale and center fractal
###################################
class ScalingTool
attr_reader :max_height, :max_width, :lowest_y, :lowest_x, :highest_y, :highest_x, :raw_data, :scale
def initialize max_width, max_height, data = []
@max_width = max_width - BORDER
@max_height = max_height - BORDER
@raw_data = data
@lowest_x = 0
@lowest_y = 0
@highest_x = 0
@highest_y = 0
@scale = 1
end
def scale_to_fit
processed = raw_data
scale = calculate_scale_factor
processed.each do |item|
item[XPOS] = scale * (item[XPOS] - lowest_x) + BORDER/2
item[YPOS] = scale * (item[YPOS] - lowest_y) + BORDER/2
end
return processed
end
private
def calculate_x_range
@raw_data.each do |item|
@lowest_x = item[XPOS] unless lowest_x < item[XPOS]
@highest_x = item[XPOS] unless highest_x > item[XPOS]
end
highest_x - lowest_x
end
def calculate_y_range
@raw_data.each do |item|
@lowest_y = item[YPOS] unless lowest_y < item[YPOS]
@highest_y = item[YPOS] unless highest_y > item[YPOS]
end
highest_y - lowest_y
end
#################################################
# Returns the smallest of width or height factors
# as side effect stores lowest x and y values
#################################################
def calculate_scale_factor
scale_x = (max_height * 1.0)/calculate_x_range
scale_y = (max_width * 1.0)/calculate_y_range
(scale_x < scale_y) ? scale_x : scale_y
end
end
Labels:
l-system fractal,
transformation
Thursday, 11 February 2010
A Pentive? Fractal
The pentive fractal is another space filling fractal that I found over on a fractint site again for the grammar generator see my Cesàro fractal.
########################################################
# A Pentive fractal implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
########################################################
require 'pentive'
class Pentive_Test < Processing::App
attr_reader :pentive,:points, :production
def setup
size(600, 400)
@pentive = Pentive.new(width/95, height*0.9)
@production = pentive.create_grammar(8)
@points = pentive.translate_rules(production)
no_loop()
end
def draw()
background(0)
stroke(255)
points.each do |tmp|
line(*tmp)
end
end
end
####################################################
# The Pentive? fractal
####################################################
class Pentive
attr_reader :draw_length, :xpos, :ypos, :theta, :axiom, :grammar, :delta
DELTA = Math::PI/180 * 36 # 36 degrees
def initialize xpos, ypos
@axiom = "Q"
@theta = -DELTA
@grammar = Grammar.new(axiom)
grammar.add_rule("F", "")
grammar.add_rule("P","1-FR3+FS1-FU") # abbreviated grammar 1 = two & 3 = four repeats
grammar.add_rule("Q", "FT1+FR3-FS1+")
grammar.add_rule("R", "1+FP3-FQ1+FT")
grammar.add_rule("S", "FU1-FP3+FQ1-")
grammar.add_rule("T", "+FU1-FP+")
grammar.add_rule("U", "-FQ1+FT-")
@draw_length = 12
@xpos = xpos
@ypos = ypos
end
def create_grammar(gen)
grammar.generate(gen)
end
def translate_rules(prod)
repeats = 1
points = [] # An empty array to store lines as an array of points
prod.scan(/./) do |ch|
case(ch)
when "F"
temp = [xpos, ypos, (@xpos += draw_length * Math.cos(theta)), (@ypos += draw_length * Math.sin(theta))]
points.push(temp)
when "+"
@theta += DELTA * repeats
repeats = 1
when "-"
@theta -= DELTA * repeats
repeats = 1
when '1', '3'
repeats += Integer(ch)
when "P", "Q", "R", "S", "T", "U"
else
puts("character '#{ch}' not in grammar")
end
end
return points
end
end
########################################################
# A Pentive fractal implemented using a
# Lindenmayer System in ruby-processing by Martin Prout
########################################################
require 'pentive'
class Pentive_Test < Processing::App
attr_reader :pentive,:points, :production
def setup
size(600, 400)
@pentive = Pentive.new(width/95, height*0.9)
@production = pentive.create_grammar(8)
@points = pentive.translate_rules(production)
no_loop()
end
def draw()
background(0)
stroke(255)
points.each do |tmp|
line(*tmp)
end
end
end
####################################################
# The Pentive? fractal
####################################################
class Pentive
attr_reader :draw_length, :xpos, :ypos, :theta, :axiom, :grammar, :delta
DELTA = Math::PI/180 * 36 # 36 degrees
def initialize xpos, ypos
@axiom = "Q"
@theta = -DELTA
@grammar = Grammar.new(axiom)
grammar.add_rule("F", "")
grammar.add_rule("P","1-FR3+FS1-FU") # abbreviated grammar 1 = two & 3 = four repeats
grammar.add_rule("Q", "FT1+FR3-FS1+")
grammar.add_rule("R", "1+FP3-FQ1+FT")
grammar.add_rule("S", "FU1-FP3+FQ1-")
grammar.add_rule("T", "+FU1-FP+")
grammar.add_rule("U", "-FQ1+FT-")
@draw_length = 12
@xpos = xpos
@ypos = ypos
end
def create_grammar(gen)
grammar.generate(gen)
end
def translate_rules(prod)
repeats = 1
points = [] # An empty array to store lines as an array of points
prod.scan(/./) do |ch|
case(ch)
when "F"
temp = [xpos, ypos, (@xpos += draw_length * Math.cos(theta)), (@ypos += draw_length * Math.sin(theta))]
points.push(temp)
when "+"
@theta += DELTA * repeats
repeats = 1
when "-"
@theta -= DELTA * repeats
repeats = 1
when '1', '3'
repeats += Integer(ch)
when "P", "Q", "R", "S", "T", "U"
else
puts("character '#{ch}' not in grammar")
end
end
return points
end
end
Labels:
l-system fractal,
ruby processing
Subscribe to:
Comments (Atom)
Followers
About Me
- monkstone
- I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2


