
#!/usr/bin/env ruby -w
#---------------------------------------------------------------------#
# #
# Program : ifs.rb #
# Object : Iterated Function System class. ( Fractal -- IFS ) #
# Author : David Tran #
# Version : 2007-06-18 #
# Blog : http://davidtran.doublegifts.com/blog/?p=82 #
# http://davidtran.doublegifts.com/blog/?p=48 #
# http://davidtran.doublegifts.com/blog/?p=25 #
# Reference : http://en.wikipedia.org/wiki/Iterated_function_system #
# http://en.wikipedia.org/wiki/Sierpinski_gasket #
# http://www.sewanee.edu/Physics/PHYSICS123/IFS.html #
# http://www.alpheccar.org/en/posts/show/69 #
# #
#---------------------------------------------------------------------#
class IFS
#
# Each rule is an array of [w, a, b, c, d, e, f] where
# X' = aX + bY + e
# Y' = cX + dY + f
# and w is weight (probability) for the rule.
#
def initialize(rules, start_point=[0.0, 0.0])
@point = start_point
@rules = rules
# change weight to range
@rules.inject(0.0) do |weight_begin, r|
weight_end = weight_begin + r[0]
r[0] = weight_begin ... weight_end
weight_end
end
end
def get_next_points(nb)
(1..nb).inject([]) { |pts, _| pts << get_next_point }
end
def get_next_point
w, a, b, c, d, e, f = get_rule
x, y = @point
@point = [ a*x + b*y + e, c*x + d*y + f ]
end
def get_rule
loop do
random = rand
@rules.each { |r| return r if r[0].include?(random) }
end
end
end
if __FILE__ == $0
require 'RMagick'
def fractal_gif(name, width, height, color, imgs, pts_per_img, rules, transform)
ifs = IFS.new(rules)
imageList = Magick::ImageList.new
image = Magick::Image.new(width, height)
image.delay = 30
imageList << image
imgs.times do
image = image.copy
pts_per_img.times do
x, y = transform[*ifs.get_next_point]
image.pixel_color(x, y, color)
end
imageList << image
end
imageList.write(name + ".gif")
end
def fractal_jpg(name, width, height, color, points, rules, transform)
ifs = IFS.new(rules)
image = Magick::Image.new(width, height)
points.times do
x, y = transform[*ifs.get_next_point]
image.pixel_color(x, y, color)
end
image.write(name + ".jpg")
end
puts "ifs_fern.jpg ..."
fractal_jpg('ifs_fern', 300, 500, 'green', 70000,
[ [0.01, 0.00, 0.00, 0.00, 0.16, 0, 0.00],
[0.08, 0.20, -0.26, 0.23, 0.22, 0, 1.60],
[0.08, -0.15, 0.28, 0.26, 0.24, 0, 0.44],
[0.74, 0.75, 0.04, -0.04, 0.85, 0, 1.60]
],
proc { |x, y| [x * 40 + 150, 450 - y * 40] }
)
puts "ifs_fern.gif ..."
fractal_gif('ifs_fern', 300, 350, 'green', 50, 1000,
# -5 <= x <= 5 and 0 <= y <= 10
[ [0.01, 0.00, 0.00, 0.00, 0.16, 0, 0.00],
[0.07, 0.20, -0.26, 0.23, 0.22, 0, 1.60],
[0.07, -0.15, 0.28, 0.26, 0.24, 0, 0.44],
[0.85, 0.85, 0.04, -0.04, 0.85, 0, 1.60]
],
proc { |x, y| [x * 30 + 150, 325 - y * 30] }
)
puts "ifs_koch_curve.gif ..."
fractal_gif('ifs_koch_curve', 400, 135, 'black', 50, 200,
[ [0.25, 0.333, 0.000, 0.000, 0.333, 0.000, 0.000],
[0.25, 0.167, -0.287, 0.287, 0.167, 0.333, 0.000],
[0.25, 0.167, 0.287, -0.287, 0.167, 0.500, 0.287],
[0.25, 0.333, 0.000, 0.000, 0.333, 0.667, 0.000]
],
proc { |x, y| [x*400, 130 - y*400] }
)
puts "ifs_sierpinski_triangle.gif ..."
fractal_gif('ifs_sierpinski_triangle', 400, 300, 'magenta', 50, 1000,
[ [0.33, 0.5, 0, 0, 0.5, 0.0, 0.0],
[0.33, 0.5, 0, 0, 0.5, 0.5, 0.5],
[0.33, 0.5, 0, 0, 0.5, 1.0, 0.0]
],
proc { |x, y| [x*200, 250 - y*200] }
)
puts "ifs_sierpinski_carpet.gif ..."
fractal_gif('ifs_sierpinski_carpet', 200, 200, 'blue', 50, 500,
[ [0.125, 0.333, 0, 0, 0.333, 0.000, 0.000],
[0.125, 0.333, 0, 0, 0.333, 0.333, 0.000],
[0.125, 0.333, 0, 0, 0.333, 0.666, 0.000],
[0.125, 0.333, 0, 0, 0.333, 0.000, 0.333],
[0.125, 0.333, 0, 0, 0.333, 0.000, 0.666],
[0.125, 0.333, 0, 0, 0.333, 0.333, 0.666],
[0.125, 0.333, 0, 0, 0.333, 0.666, 0.333],
[0.125, 0.333, 0, 0, 0.333, 0.666, 0.666],
],
proc { |x, y| [x*200, y*200] }
)
end



