├── README.textile └── tunnel.rb /README.textile: -------------------------------------------------------------------------------- 1 | h2. Tunnel Algorithm in Ruby Processing 2 | 3 | !http://dl.getdropbox.com/u/221414/blogs/tunnel.png! 4 | 5 | "Video":http://screencast.com/t/PBWXTvJdVfP 6 | -------------------------------------------------------------------------------- /tunnel.rb: -------------------------------------------------------------------------------- 1 | # Tunnel 2 | 3 | class Tunnel < Processing::App 4 | 5 | module Lookup 6 | def self.generate 7 | @sin_table = [] 8 | @cos_table = [] 9 | 10 | @precision = 0.2 11 | @inverse = 1.0 / @precision 12 | @table_length = (360.0 * @inverse).to_i 13 | 14 | @direction_x = 5 15 | @direction_y = 0 16 | 17 | (0..@table_length).each do |i| 18 | @sin_table[i] = Math.sin i * (Math::PI / 180) * @precision 19 | @cos_table[i] = Math.cos i * (Math::PI / 180) * @precision 20 | end 21 | end 22 | 23 | def self.change_direction 24 | @direction_x += 0.5 - rand if rand > 0.98 25 | @direction_y += 0.5 - rand if rand > 0.98 26 | end 27 | 28 | def self.sin_table 29 | @sin_table 30 | end 31 | 32 | def self.cos_table 33 | @cos_table 34 | end 35 | 36 | def self.table_length 37 | @table_length 38 | end 39 | 40 | def self.inverse 41 | @inverse 42 | end 43 | 44 | def self.direction_x ; @direction_x ; end 45 | def self.direction_y ; @direction_y ; end 46 | end 47 | 48 | class Ring 49 | attr_reader :diameter, :offset 50 | 51 | def initialize(x, y, diameter, offset, width, height) 52 | @x, @y, @diameter, @offset = x, y, diameter, offset 53 | @step = 10 54 | @width = width 55 | @height = height 56 | # Set these to 1 + rand(3) for a nice effect in no delete mode 57 | @grow_speed = 2.0 58 | @spin_speed = 2.5 59 | @base_colour = color(80, 30, 30) 60 | 61 | @direction_x = Lookup.direction_x 62 | @direction_y = Lookup.direction_y 63 | end 64 | 65 | def update(base_colour = nil) 66 | @base_colour = base_colour if base_colour 67 | 68 | # Needs to get faster as it gets wider 69 | @diameter += @grow_speed + (@diameter * Lookup.sin_table[(@diameter % Lookup.table_length) / 8]) 70 | @offset += @spin_speed 71 | 72 | # These can wrap, but this isn't used in tunnel mode 73 | @diameter = 0 if @diameter >= Lookup.table_length 74 | @offset = 0 if @offset >= Lookup.table_length 75 | 76 | @x = @x + (Lookup.cos_table[@offset] * @direction_x) 77 | @y = @y + (Lookup.sin_table[@offset] * @direction_y) 78 | end 79 | 80 | def draw 81 | radius = 16 + @height * Lookup.sin_table[@diameter] 82 | 83 | (0..360/@step).each do |i| 84 | i *= @step 85 | theta = (i * Lookup.inverse + @offset) % Lookup.table_length 86 | 87 | x = @x + (Lookup.cos_table[theta] * radius) 88 | y = @y + (Lookup.sin_table[theta] * radius) 89 | 90 | plot x, y 91 | end 92 | end 93 | 94 | def plot(x, y) 95 | set x, y, @base_colour 96 | end 97 | end 98 | 99 | def setup 100 | color_mode RGB, 255 101 | background 0 102 | 103 | Lookup.generate 104 | 105 | @ring_size_limit = 256 106 | @palette = make_palette 107 | @rings = [] 108 | 109 | add_ring 1, 1 110 | 111 | frame_rate 30 112 | end 113 | 114 | def draw 115 | background 0 116 | @rings.each do |ring| 117 | # The widest the ring can be needs to be calculated here 118 | colour_index = (ring.diameter.to_f / 60) * 255 119 | base_colour = @palette[colour_index] 120 | ring.update(base_colour) 121 | ring.draw 122 | end 123 | 124 | @rings.delete_if { |ring| ring.diameter > 400 } 125 | 126 | if @rings.size < @ring_size_limit and @rings.last.diameter > 6 127 | Lookup.change_direction 128 | add_ring 0, @rings.last.offset + 2 129 | end 130 | end 131 | 132 | def add_ring(diameter, offset) 133 | @rings << Ring.new(width / 2, height / 2, diameter, offset, width, height) 134 | end 135 | 136 | def make_palette 137 | palette = [] 138 | # Create the bands of colour for the palette (256 is the maximum colour) 139 | limit = 256 140 | base_colour = 1 141 | (0..limit).each do |i| 142 | offset = base_colour + i 143 | offset = 255 if offset > 255 144 | palette[i] = [base_colour, base_colour, offset] 145 | 146 | base_colour += Lookup.sin_table[i] 147 | end 148 | palette.collect { |p| color(*p) } 149 | end 150 | end 151 | 152 | Tunnel.new :title => "Tunnel", :width => 640, :height => 480 153 | --------------------------------------------------------------------------------