Created
November 27, 2016 16:16
-
-
Save JoshCheek/7e333067da09bc286858c86c78a9f860 to your computer and use it in GitHub Desktop.
Parametric L Systems
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'graphics' | |
require 'strscan' | |
class LSystem | |
attr_accessor :rules, :cache | |
def initialize(start:, rules:) | |
self.rules = rules | |
self.cache = [[start]] | |
end | |
def at_depth(n) | |
return cache[n] if n < cache.length | |
cache[n] = at_depth(n-1).flat_map do |fn, *args| | |
if rules.key?(fn) | |
rules[fn].call(*args) # substitute | |
else | |
[[fn, *args]] # do not substitute | |
end | |
end | |
end | |
end | |
class Triangle < Graphics::Simulation | |
include Math | |
def π | |
PI | |
end | |
def º2ø(º) | |
º * π / 180 | |
end | |
def initialize | |
width = 800 | |
height = 600 | |
bits_per_pixel = 24 | |
super width, height, bits_per_pixel | |
end | |
def draw(n) | |
return if n > 14 | |
stack = [] | |
stride = h/2 - 30 | |
x = w/2 | |
y = 30 | |
ø = π/2 | |
∆ø = º2ø(90) | |
colour = :white | |
# http://algorithmicbotany.org/papers/abop/abop-ch1.pdf | |
# pages 48 - 50 | |
r = 1.456 | |
@triangle_system ||= LSystem.new( | |
start: [:A, 1], | |
rules: { | |
A: -> s {[ | |
[:F, s], | |
[:"["], [:+], [:A, s/r], [:"]"], | |
[:"["], [:-], [:A, s/r], [:"]"], | |
]} | |
} | |
) | |
@triangle_system.at_depth(n).each do |fn_name, *args| | |
case fn_name | |
when :A | |
# noop | |
when :F | |
multipler = args[0] | |
distance = stride * multipler | |
old_x = x | |
old_y = y | |
x = x + distance*cos(ø) | |
y = y + distance*sin(ø) | |
line(old_x, old_y, x, y, colour) | |
when :+ | |
ø += ∆ø | |
when :- | |
ø -= ∆ø | |
when :"[" | |
stack.push [x, y, ø] | |
when :"]" | |
x, y, ø = stack.pop | |
else | |
raise "wat: #{fn_name.inspect} #{args.map(&:inspect).join(', ')}" | |
end | |
end | |
end | |
end | |
Triangle.new.run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'graphics' | |
# p47 - p48 of http://algorithmicbotany.org/papers/abop/abop-ch1.pdf | |
class Triangle < Graphics::Simulation | |
include Math | |
def initialize | |
width = 800 | |
height = 600 | |
bits_per_pixel = 24 | |
super width, height, bits_per_pixel | |
end | |
def draw(iteration) | |
return if iteration > 6 | |
clear :white | |
stride = 400 # how long the base line is | |
x = 100 # from left | |
y = 100 # from bottom | |
ø = 0 # facing right | |
∆ø = 86*PI/180 # 86º places gaps in the result (1.37a) | |
# ∆ø = 90*PI/180 # 90º everything is flush (1.37b) | |
c = 1 | |
p = 0.3 | |
q = c - p | |
h = (p * q) ** 0.5 | |
instructions = [['F', 1]] | |
rules = { | |
'F' => lambda do |x| | |
[ ['F', x * p], | |
['+'], | |
['F', x * h], | |
['-'], | |
['-'], | |
['F', x * h], | |
['+'], | |
['F', x * q], | |
] | |
end | |
} | |
iteration.times do | |
instructions = instructions.flat_map do |fn, *args| | |
if rules.key?(fn) | |
rules[fn].call(*args) # substitute | |
else | |
[[fn, *args]] # do not substitute | |
end | |
end | |
end | |
instructions.each do |fn_name, *args| | |
case fn_name | |
when "A" | |
# noop | |
when "F" | |
multipler = args[0] | |
distance = stride * multipler | |
old_x = x | |
old_y = y | |
x = x + distance*cos(ø) | |
y = y + distance*sin(ø) | |
line(old_x, old_y, x, y, :black) | |
when "+" | |
ø += ∆ø | |
when "-" | |
ø -= ∆ø | |
when "[" | |
stack.push [x, y, ø] | |
when "]" | |
x, y, ø = stack.pop | |
else | |
raise "wat: #{fn_name.inspect} #{args.inspect}" | |
end | |
end | |
end | |
end | |
Triangle.new.run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# encoding: utf-8 | |
# p47 - p48 of http://algorithmicbotany.org/papers/abop/abop-ch1.pdf | |
require 'graphics' | |
class Triangle < Graphics::Simulation | |
include Math | |
def initialize | |
width = 800 | |
height = 600 | |
bits_per_pixel = 24 | |
super width, height, bits_per_pixel | |
∆ø = 86*PI/180 # 86º places gaps in the result (1.37a) | |
# ∆ø = 90*PI/180 # 90º everything is flush (1.37b) | |
ø = 0 # direction the turtle is facing | |
x = 50 # left margin | |
y = 200 # bottom margin | |
stride = 450 # how long the base line is | |
c = 1 | |
p = 0.3 | |
q = c - p | |
h = (p * q) ** 0.5 | |
instructions = [['F', 1, 0]] | |
rules = { | |
'F' => lambda do |x, t| | |
# It splits the triangle into 4 parts, like this: | |
# | |
# /C\ | |
# / | \ | |
# / | 3 /\. | |
# / 2 | / \. | |
# / \ | / 4 \. | |
# / 1 \|/ \ | |
# A------B---------------D | |
# | |
# And it move like this: | |
# A->B for 0.3 (this will spawn triangle 1 later) | |
# turn left to face C | |
# B->C for 0.7 (this will spawn triangle 2 later) | |
# turn right twice to face B | |
# C->B for 0.7 (this will spawn triangle 3 later) | |
# turn left to face D | |
# B->D for 0.7 (this will spawn triangle 4 later) | |
# | |
# "t" is a timer it uses to delay writing triangle 1 for 2 iterations | |
# and triangles 2 and 3 for 1 iteration. I assume this number is picked | |
# because it will take 3 more iterations before triangle 4's subtraingle | |
# at D is the same size as 1. Thus it will be relatively sparse by comparison. | |
# I don't really understand how delaying 1 fixes this since that subtraingle | |
# will be delayed, as well. I suspect the answer is just that at any given point, | |
# the things line up such that the difference is minimized. | |
if t == 0 | |
[ ['F', x * p, 2], | |
['+'], | |
['F', x * h, 1], | |
['-'], | |
['-'], | |
['F', x * h, 1], | |
['+'], | |
['F', x * q, 0], | |
] | |
else | |
[['F', x, t-1]] | |
end | |
end | |
} | |
10.times do | |
instructions = instructions.flat_map do |fn, *args| | |
if rules.key?(fn) | |
rules[fn].call(*args) # substitute | |
else | |
[[fn, *args]] # do not substitute | |
end | |
end | |
end | |
@draw_function = lambda do | |
instructions.each do |fn_name, *args| | |
case fn_name | |
when "A" | |
# noop | |
when "F" | |
multipler = args[0] | |
distance = stride * multipler | |
old_x = x | |
old_y = y | |
x = x + distance*cos(ø) | |
y = y + distance*sin(ø) | |
line(old_x, old_y, x, y, :black) | |
when "+" | |
ø += ∆ø | |
when "-" | |
ø -= ∆ø | |
when "[" | |
stack.push [x, y, ø] | |
when "]" | |
x, y, ø = stack.pop | |
else | |
raise "wat: #{fn_name.inspect} #{args.inspect}" | |
end | |
end | |
end | |
end | |
def draw(n) | |
return if n > 1 | |
clear :white | |
@draw_function.call | |
end | |
end | |
Triangle.new.run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment