#!/usr/bin/env ruby # By T. Kowaliw, 2014, http://kowaliw.ca # Feel free to use, modify, copy, distribute, sell, abuse, exploit, enslave, ravage, whatevs, provided you mail me one (1) live gerbil. class Cell include Math attr_accessor :x, :y def initialize(parent, x, y, size, direction, brightness) @parent = parent @x = x @y = y @size = size @direction = direction @brightness = brightness end def children # most of the time, we will just add another child in a direct line from the parent... if rand < 0.7 new_x = @x + cos(@direction)*@size*5 new_y = @y + sin(@direction)*@size*5 new_direction = @direction new_direction = new_direction + (rand*0.4 - 0.2) if rand < 0.1 new_direction = new_direction + (rand*1.4 - 0.7) if rand < 0.02 new_size = [@size - 0.25 + rand*0.2, 1.0].max new_size = [@size +rand - 0.5, 1.0].max if rand < 0.05 return [Cell.new(self, new_x, new_y, new_size, new_direction, [@brightness + 0.03, 0.9].min)] # sometimes, let's divide elsif rand < 0.9 new_direction_1 = @direction + 0.3 new_direction_2 = @direction - 0.3 new_x_1 = @x + cos(new_direction_1)*@size*5 new_y_1 = @y + sin(new_direction_1)*@size*5 new_x_2 = @x + cos(new_direction_2)*@size*5 new_y_2 = @y + sin(new_direction_2)*@size*5 return [ Cell.new(self, new_x_1, new_y_1, [@size - 0.25, 1.0].max, new_direction_1, [@brightness + 0.03, 0.9].min), Cell.new(self, new_x_2, new_y_2, [@size - 0.25, 1.0].max, new_direction_2, [@brightness + 0.03, 0.9].min), ] # and occasionally, let's just kill the whole branch. else return [] end end # return a line to the joint (if one exists) and then a ball at the joint. def to_svg scaled_colour_val = (@brightness*255).to_i the_colour = "rgb(#{scaled_colour_val},#{scaled_colour_val},#{scaled_colour_val})" rect_str = "" if @parent return "#{rect_str} " end end # one array for the finished joints, and one for thos that might still divide. finished_cells = Array.new to_process_cells = Array.new # start with a single cell at the bottom centre pointing "up" (NB: svg canvas has zero at the top) root_cell = Cell.new(nil, 150.0, 290.0, 4.0, -1.57079, 0.1) to_process_cells.push(root_cell) # for twenty iterations, process cells and add the processed ones to the "finished" queue 1..20.times do new_to_process_cells = Array.new to_process_cells.each do |curr_cell| finished_cells.push(curr_cell) curr_cell.children.each { |kid| new_to_process_cells.push(kid) } end to_process_cells = new_to_process_cells end # reverse so that last added cells are drawn first. Then lines will always be drawn before circles. finished_cells = finished_cells.concat(to_process_cells).reverse # now output the SVG code puts "" puts "" finished_cells.each {|curr_cell| puts curr_cell.to_svg} puts ""