#!/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 create one (1) piece of graffiti in my honour in the major city of your choice. Proof required.
require 'colormath'
class VirgiCell
include Math, ColorMath
attr_accessor :x, :y
def initialize(parent, x, y, size, direction, hue, saturation, brightness, iter_num)
@parent = parent
@x = x
@y = y
@size = size
@direction = direction
@brightness = brightness
@hue = hue
@saturation = saturation
@iter_num = iter_num
end
def children
the_die = rand
new_b = [@brightness + 0.005, 0.8].min
new_h = @hue + rand*0.01 - 0.005
return [] if @size < 1.2 && rand < 0.2 # kill small branches
the_die -= 0.1 if @size < 1.5 # less small branches from small brances
# most of the time, we will just add another child in a direct line from the parent...
if the_die < 0.55 || @iter_num < 2
immediate_direction = @direction
immediate_direction = @direction + rand*3.14 - 1.57 if rand < 0.6 # generate a different immediate direction for gnarls
size_mult = @size*rand*3
size_mult = @size*4.0 if rand < 0.1 && @iter_num < 5
new_x = @x + cos(immediate_direction)*size_mult
new_y = @y + sin(immediate_direction)*size_mult
new_direction = @direction
new_direction = new_direction + (rand*0.2 - 0.1) if rand < 0.4
new_direction = new_direction + (rand*0.6 - 0.3) if rand < 0.05
new_size = [@size + rand*0.2 - 0.12, 1.0].max
new_size = [@size +rand*2.0 - 1.2 , 1.0].max if rand < 0.05
return [VirgiCell.new(self, new_x, new_y, new_size, new_direction, new_h, @saturation, new_b, @iter_num+1)]
# sometimes, let's divide into equal sized branches
elsif the_die < 0.75
new_direction_1 = @direction + 0.3
new_direction_2 = @direction - 0.3
new_x_1 = @x + cos(new_direction_1)*@size*3
new_y_1 = @y + sin(new_direction_1)*@size*3
new_x_2 = @x + cos(new_direction_2)*@size*3
new_y_2 = @y + sin(new_direction_2)*@size*3
return [
VirgiCell.new(self, new_x_1, new_y_1, [@size*0.75, 1.0].max, new_direction_1, new_h, @saturation, new_b, @iter_num+1),
VirgiCell.new(self, new_x_2, new_y_2, [@size*0.75, 1.0].max, new_direction_2, @hue, @saturation, new_b, @iter_num+1)
]
# sometimes, let's shoot off a tiny branch in one direction
elsif the_die < 0.96
new_direction_1 = @direction + rand*0.05 - 0.025
new_direction_2 = @direction - rand*1.27 - 0.3
new_direction_2 = @direction + rand*1.27 + 0.3 if rand < 0.5
new_x_1 = @x + cos(new_direction_1)*@size*3
new_y_1 = @y + sin(new_direction_1)*@size*3
new_x_2 = @x + cos(new_direction_2)*@size*4
new_y_2 = @y + sin(new_direction_2)*@size*4
return [
VirgiCell.new(self, new_x_1, new_y_1, @size, new_direction_1, new_h, @saturation, new_b, @iter_num+1),
VirgiCell.new(self, new_x_2, new_y_2, [(rand*2 + 1),@size/3].min, new_direction_2, @hue, @saturation, new_b, @iter_num+1)
]
end
# and the rest of the time, 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 = "