Monday, March 16, 2009

Sand Chaser




Built a screen saver with the help of a wonderful new gem called ruby-processing. I've been messing around with Processing in java for a little bit as friendly intro to opengl effects and shaders.
Was very happy to find such an advanced ruby gem that leverages both the power of jruby and processing.


Check out the java version on OpenProcessing.org

Compare with the JRuby Applet. (Can take a minute to load)

Here's the ruby code:

    1 require 'ruby-processing'
2
3 class SandChaser < Processing::App
4 include Math
5 attr_reader :grains, :num_grains
6
7 def setup (dx=0, dy=0, size=150)
8 srand Time.now.to_i
9 @dx, @dy = 0, 0
10 hint(ENABLE_OPENGL_4X_SMOOTH )
11 @num_grains = size
12 reset_all
13 end
14
15 def draw
16 count = 0
17 @grains.each {|grain| count += grain.move }
18 if count == @grains.size
19 sleep 3
20 reset_all
21 end
22 end
23
24 def mouse_pressed
25 reset_all
26 end
27
28 def reset_all
29 @grains = []
30 @num_grains = ((rand 5)+2) *(10**((rand 2)+1))
31 #start with two source colors
32 @source_colors = [[rand(255), rand(255), rand(255), rand(30)+45],
33 [rand(255), rand(255), rand(255), rand(30)+45]]
34 #set the backgrond color to black
35 background 0
36 @num_grains.times do |i|
37 # initialize the sand grains with a
38 # random vector going in any direction
39 # starting from the center of the screen.
40 vx = (rand * 2) - 1 # random number between -1 and 1
41 vy = (rand * 2) - 1 # random number between -1 and 1
42 #magnitude multiplier based on the screen size
43 k = ((width+height)/2)/5.to_f
44 @grains[i] = Grain.new(width/2, height/2, k*vx, k*vy, i)
45 # vvt -= 0.00033
46 # vt += vvt
47 end
48 @grains.each { |grain| grain.find_target }
49 end
50
51 def some_color
52 #randomly pick from one of the 2 source colors.
53 choice = @source_colors[rand(2)].dup
54 #boost the 4 elements of the color by upto 60
55 choice.map! { |c| c + rand * (60) }
56 # decrease the alpha by 30percent.
57 choice[3] = choice[3] * 0.7
58 #introduce random colors a 5th of the time
59 return choice
60 end
61 end
62
63 SandChaser.new :width => 500, :height => 258,
64 :title => "Sand Chaser", :full_screen => true
65
66 class Grain
67 include Math
68 attr_reader :x, :y, :color, :index
69
70 def initialize(x, y, vx, vy, index)
71 @app = Processing::App.current
72 @x, @y = x, y
73 @vx, @vy = vx, vy
74 @index = index
75 @color = @app.some_color
76 end
77
78 def move
79 #approach the target
80 if ( (@vx.abs + @vy.abs) > 0.001 &&
81 @x.between?(0, @app.width) &&
82 @y.between?(0, @app.height)
83 )
84 @vx += (@target.x - @x)
85 @vy += (@target.y - @y)
86 newX = @x + @vx*1/60.to_f; newY = @y + @vy*1/60.to_f
87 draw_from_point_to_point(@x, @y, newX, newY)
88 @x, @y = newX, newY
89 return 0
90 end
91 return 1
92 end
93
94 def draw_from_point_to_point( fromX, fromY, toX, toY)
95 # draw points between the from and to coordinates
96 # to smooth out the effect.
97 smoothing_factor = 1
98 numberOfIncrements = @app.dist(fromX, fromY, toX, toY)
99 numberOfIncrements*=smoothing_factor.to_f
100
101 xDistance = ([fromX, toX].max - [fromX, toX].min)
102 xincrement = xDistance/numberOfIncrements.to_f
103 yDistance = ([fromY, toY].max - [fromY, toY].min)
104 yincrement = yDistance/numberOfIncrements.to_f
105
106 xDir, yDir = 1, 1
107 xDir = -1 if toX < fromX
108 yDir = -1 if toY < fromY
109
110 (numberOfIncrements.to_i+1).times do |i|
111 @app.stroke *@target.color
112 @app.point fromX+(i*xDir*xincrement), fromY+(i*yDir*yincrement)
113 end
114 end
115
116 def find_target
117 #find another grain at random to follow.
118 @target = @app.grains[rand @app.grains.size]
119 end
120 end

No comments:

Post a Comment