# Penrose Tile Generator # Using a variant of the "ArrayList" recursion technique: http://natureofcode.com/book/chapter-8-fractals/chapter08_section4 # Penrose Algorithm from: http://preshing.com/20110831/penrose-tiling-explained # Daniel Shiffman May 2013 # Translated (and refactored) to ruby-processing Jan 2014 by Martin Prout load_libraries :vecmath, :tile, :control_panel attr_reader :tris, :s, :panel, :hide, :acute def setup size(1024, 576) control_panel do |c| c.title = "Tiler Control" c.look_feel "Nimbus" c.checkbox :seed c.checkbox :acute c.button :generate c.button :reset! @panel = c end @hide = false init false # defaults to regular penrose end def draw # only make control_panel visible once, or again when hide is false unless hide @hide = true panel.setVisible(hide) end background(255) translate(width/2, height/2) tris.each do |t| t.display end end def generate next_level = [] tris.each do |t| more = t.subdivide more.each do |m| next_level << m end end @tris = next_level end def reset! Tiler.acute(acute) # set the Tiler first init @seed java.lang.System.gc # but does it do any good? end def init alt_seed @tris = [] 10.times do |i| # create 36 degree segments a = Vec2D.new b = Vec2D.from_angle((2 * i - 1) * PI / 10) c = Vec2D.from_angle((2 * i + 1) * PI / 10) b *= 370 c *= 370 if alt_seed tile = (i % 2 == 0)? Tiler.tile(b, a, c) : Tiler.tile(c, a, b) tris << tile else tile = (i % 2 == 0)? Tiler.tile(a, b, c) : Tiler.tile(a, c, b) tris << tile end end end
Here is the tile library:-
module Tiler @@acute = false def self.acute(x) @@acute = x end # setup the initial tiling with all red tiles def self.tile(a, b, c) tile = (@@acute)? ATile.new(0, a, b, c) : Tile.new(0, a, b, c) end end class Tile include Processing::Proxy PHI = (1.0 + Math.sqrt(5)) / 2.0 # golden ratio RED = [255, 0, 0] BLUE = [0, 0, 255] COLORS = [RED, BLUE] attr_reader :a, :b, :c, :col def initialize(col, a, b, c) @col, @a, @b, @c = col, a, b, c end def display no_stroke fill(*COLORS[col]) triangle(a.x, a.y, b.x, b.y, c.x, c.y) #fill(0,0,255) #ellipse(a.x,a.y,4,4) #ellipse(b.x,b.y,4,4) #ellipse(c.x,c.y,4,4) end def subdivide result = [] if (col == 0) # Subdivide red triangle p = b - a p /= PHI p += a result << Tile.new(0, c, p, b) result << Tile.new(1, p, c, a) else # Subdivide blue triangle q = a - b q /= PHI q += b r = c - b r /= PHI r += b result << Tile.new(1, r, c, a) result << Tile.new(1, q, r, b) result << Tile.new(0, r, q, a) end return result end end class ATile < Tile def subdivide result = [] if (col == 0) # Subdivide red (half kite) triangle q = b - a q /= PHI q += a r = c - b r /= PHI r += b result << ATile.new(1, r, q, b) result << ATile.new(0, q, a, r) result << ATile.new(0, c, a, r) else # Subdivide blue (half dart) triangle p = a - c p /= PHI p += c result << ATile.new(1, b, p, a) result << ATile.new(0, p, c, b) end return result end end
Here is a screenshot, before I added second checkbox
No comments:
Post a Comment