#!/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 send me one hot nude photo of yourself or your pet. require 'colormath' class Cell include Math, ColorMath attr_accessor :x, :y def initialize(parent, x, y, size, direction, hue, saturation, brightness) @parent = parent @x = x @y = y @size = size @direction = direction @brightness = brightness @hue = hue @saturation = saturation 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, @hue, @saturation, [@brightness + 0.02, 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, @hue, @saturation, [@brightness + 0.01, 0.9].min), Cell.new(self, new_x_2, new_y_2, [@size - 0.25, 1.0].max, new_direction_2, @hue, @saturation, [@brightness + 0.01, 0.9].min), ] end # and occasionally, let's just kill the whole branch. return [] end # return a line to the joint (if one exists) and then a ball at the joint. def to_svg # for some idiot reason, the HSL spec calls for H in [0, 360], and the other params in [0,1]. the_colour = ColorMath::HSL.new(@hue*360.0, @saturation, @brightness).hex rect_str = "" if @parent return "#{rect_str} " end end class Tree def initialize(startX, startY, num_iterations, h, s, l, size) @finished_cells = Array.new # start with a single cell at the bottom centre pointing "up" (NB: svg canvas has zero at the top) @to_process_cells = [Cell.new(nil, startX, startY, size, -1.57079, h, s, l)] # for twenty iterations, process cells and add the processed ones to the "finished" queue 1..num_iterations.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 end def to_svg ret_str = "" @finished_cells.each {|curr_cell| ret_str = ret_str + curr_cell.to_svg + "\n"} return ret_str end end # now output the SVG code puts "" puts "" #puts "" central_h = 0.6 # background 1..60.times do h_val = central_h + rand*0.1 - 0.1 num_it = (15 + rand*8).to_i startX = rand*1000 startY = 250 + rand*20 size = 2.0 + rand*1.0 puts Tree.new(startX, startY, num_it, h_val, 0.1, 0.1, size).to_svg end # midground 1..10.times do h_val = central_h + rand*0.1 - 0.1 num_it = (15 + rand*8).to_i startX = rand*1000 startY = 270 + rand*10 size = 3.0 + rand*1.0 puts Tree.new(startX, startY, num_it, h_val, 0.2, 0.15, size).to_svg end # foreground 1..10.times do h_val = central_h + rand*0.1 - 0.1 num_it = (18 + rand*4).to_i startX = rand*1000 startY = 290 + rand*5 size = 4.0 + rand*1.0 puts Tree.new(startX, startY, num_it, h_val, 0.25, 0.25, size).to_svg end puts ""