graphics 1.0.0b5 → 1.0.0b6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,8 @@
3
3
  require "graphics"
4
4
 
5
5
  class TargetSimulation < Graphics::Simulation
6
+ CLEAR_COLOR = :darker_green
7
+
6
8
  def initialize
7
9
  super 640, 640, 16, "Target Practice"
8
10
 
@@ -12,7 +14,7 @@ class TargetSimulation < Graphics::Simulation
12
14
  end
13
15
 
14
16
  def draw n
15
- clear :darker_green
17
+ clear
16
18
 
17
19
  (0..640).step(64).each do |r|
18
20
  hline r, :dark_green
@@ -0,0 +1,243 @@
1
+ require "graphics"
2
+ require "graphics/rainbows"
3
+
4
+ class Float
5
+ ##
6
+ # A floating-point friendly `between?` function that excludes
7
+ # the lower bound.
8
+ # Equivalent to `min < x <= max`
9
+ ##
10
+ def xbetween? min, max
11
+ min < self && self <= max
12
+ end
13
+ end
14
+
15
+ class Particle
16
+ attr_accessor :density, :position, :velocity,
17
+ :pressure_force, :viscosity_force
18
+ def initialize pos
19
+ # Scalars
20
+ @density = 0
21
+
22
+ # Forces
23
+ @position = pos
24
+ @velocity = V::ZERO
25
+ @pressure_force = V::ZERO
26
+ @viscosity_force = V::ZERO
27
+ end
28
+ end
29
+
30
+ class SPH
31
+ ##
32
+ # Constants
33
+ #
34
+
35
+ MASS = 5 # Particle mass
36
+ DENSITY = 1 # Rest density
37
+ GRAVITY = V[0, -0.5]
38
+ H = 1 # Smoothing cutoff- essentially, particle size
39
+ K = 20 # Temperature constant- higher means particle repel more strongly
40
+ ETA = 1 # Viscosity constant- higher for more viscous
41
+
42
+ attr_reader :particles
43
+
44
+ def initialize
45
+ # Instantiate particles!
46
+ @particles = []
47
+ (0..10).each do |x|
48
+ (0..10).each do |y|
49
+ jitter = rand * 0.1
50
+ particles << Particle.new(V[x+1+jitter, y+5])
51
+ end
52
+ end
53
+ end
54
+
55
+ ##
56
+ # A weighting function (kernel) for the contribution of each neighbor
57
+ # to a particle's density. Forms a nice smooth gradient from the center
58
+ # of a particle to H, where it's 0
59
+ #
60
+
61
+ def weight r, h
62
+ len_r = r.magnitude
63
+
64
+ if len_r.xbetween? 0, h
65
+ 315.0 / (64 * Math::PI * h**9) * (h**2 - len_r**2)**3
66
+ else
67
+ 0.0
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Gradient ( that is, V(dx, dy) ) of a weighting function for
73
+ # a particle's pressure. This weight function is spiky (not flat or
74
+ # smooth at x=0) so particles close together repel strongly.
75
+ #
76
+
77
+ def gradient_weight_spiky r, h
78
+ len_r = r.magnitude
79
+
80
+ if len_r.xbetween? 0, h
81
+ r * (45.0 / (Math::PI * h**6 * len_r)) * (h - len_r)**2 * (-1.0)
82
+ else
83
+ V::ZERO
84
+ end
85
+ end
86
+
87
+ ##
88
+ # The laplacian of a weighting function that tends towards infinity when
89
+ # approching 0 (slows down particles moving faster than their neighbors)
90
+ #
91
+
92
+ def laplacian_weight_viscosity r, h
93
+ len_r = r.magnitude
94
+
95
+ if len_r.xbetween? 0, h
96
+ 45.0 / (2 * Math::PI * h**5) * (1 - len_r / h)
97
+ else
98
+ 0.0
99
+ end
100
+ end
101
+
102
+ def clear
103
+ # Clear everything
104
+ particles.each do |particle|
105
+ particle.density = DENSITY
106
+ particle.pressure_force = V::ZERO
107
+ particle.viscosity_force = V::ZERO
108
+ end
109
+ end
110
+
111
+ def calculate_density
112
+ # Calculate fluid density around each particle
113
+ particles.each do |particle|
114
+ particles.each do |neighbor|
115
+ # If particles are close together, density increases
116
+ distance = particle.position - neighbor.position
117
+
118
+ if distance.magnitude < H then
119
+ # Particles are close enough to matter
120
+ particle.density += MASS * weight(distance, H)
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ def calculate_forces
127
+ # Calculate forces on each particle based on density
128
+ particles.each do |particle|
129
+ particles.each do |neighbor|
130
+ distance = particle.position - neighbor.position
131
+ if distance.magnitude <= H then
132
+ # Temporary terms used to caclulate forces
133
+ density_p = particle.density
134
+ density_n = neighbor.density
135
+
136
+ # This *should* never happen, but it's good to check,
137
+ # because we're dividing by density later
138
+ raise "Particle density is, impossibly, 0" unless density_n != 0
139
+
140
+ # Pressure derived from the ideal gas law (constant temp)
141
+ pressure_p = K * (density_p - DENSITY)
142
+ pressure_n = K * (density_n - DENSITY)
143
+
144
+ # Navier-Stokes equations for pressure and viscosity
145
+ # (ignoring surface tension)
146
+ particle.pressure_force += gradient_weight_spiky(distance, H) *
147
+ (-1.0 * MASS * (pressure_p + pressure_n) / (2 * density_n))
148
+
149
+ particle.viscosity_force +=
150
+ (neighbor.velocity - particle.velocity) *
151
+ (ETA * MASS * (1/density_n) * laplacian_weight_viscosity(distance, H))
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ def apply_forces delta_time
158
+ # Apply forces to particles- make them move!
159
+ particles.each do |particle|
160
+ total_force = particle.pressure_force + particle.viscosity_force
161
+
162
+ # 'Eulerian' style momentum:
163
+
164
+ # Calculate acceleration from forces
165
+ acceleration = (total_force * (1.0 / particle.density * delta_time)) + GRAVITY
166
+
167
+ # Update position and velocity
168
+ particle.velocity += acceleration * delta_time
169
+ particle.position += particle.velocity * delta_time
170
+ end
171
+ end
172
+
173
+ def step delta_time
174
+ clear
175
+ calculate_density
176
+ calculate_forces
177
+ apply_forces delta_time
178
+ end
179
+
180
+ ##
181
+ # The walls nudge particles back in-bounds, plus a little jitter
182
+ # so nothing gets stuck
183
+ #
184
+
185
+ def make_particles_stay_in_bounds scale
186
+ # TODO: Better boundary conditions (THESE ARE A LAME WORKAROUND)
187
+ particles.each do |particle|
188
+ if particle.position.x >= scale - 0.01
189
+ particle.position.x = scale - (0.01 + 0.1*rand)
190
+ particle.velocity.x = 0
191
+ elsif particle.position.x < 0.01
192
+ particle.position.x = 0.01 + 0.1*rand
193
+ particle.velocity.x = 0
194
+ end
195
+
196
+ if particle.position.y >= scale - 0.01
197
+ particle.position.y = scale - (0.01+rand*0.1)
198
+ particle.velocity.y = 0
199
+ elsif particle.position.y < 0.01
200
+ particle.position.y = 0.01 + rand*0.1
201
+ particle.velocity.y = 0
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ class SimulationWindow < Graphics::Simulation
208
+ WINSIZE = 500
209
+
210
+ attr_reader :simulation, :s, :spectrum
211
+
212
+ DELTA_TIME = 0.1
213
+
214
+ def initialize
215
+ super WINSIZE, WINSIZE, 16, "Smoothed Particle Hydrodynamics"
216
+ @simulation = SPH.new
217
+ @scale = 15
218
+ @s = WINSIZE.div @scale
219
+ @spectrum = Graphics::Cubehelix.new
220
+ self.initialize_rainbow spectrum, "cubehelix"
221
+ end
222
+
223
+ def update time
224
+ simulation.step DELTA_TIME
225
+ simulation.make_particles_stay_in_bounds @scale
226
+ end
227
+
228
+ def draw time
229
+ clear
230
+
231
+ simulation.particles.each do |particle|
232
+ pos = particle.position * s
233
+ color = spectrum.clamp(particle.density*30 + 60, 0, 360).to_i
234
+
235
+ # Particles
236
+ circle(pos.x, pos.y, 5, "cubehelix_#{color}".to_sym, true)
237
+
238
+ fps time
239
+ end
240
+ end
241
+ end
242
+
243
+ SimulationWindow.new.run
@@ -55,9 +55,11 @@ class Tank < Graphics::Body
55
55
  end
56
56
  end
57
57
 
58
- def draw
59
- w.blit w.body_img, x, y, a
60
- t.draw
58
+ class View
59
+ def self.draw w, b
60
+ w.blit w.body_img, b.x, b.y, b.a
61
+ Turret::View.draw w, b.t
62
+ end
61
63
  end
62
64
 
63
65
  def turn_right; turn(-ROTATION); aim_right; end
@@ -89,8 +91,10 @@ class Turret < Graphics::Body
89
91
  self.y = y
90
92
  end
91
93
 
92
- def draw
93
- w.blit w.turret_img, x, y, a
94
+ class View
95
+ def self.draw w, b
96
+ w.blit w.turret_img, b.x, b.y, b.a
97
+ end
94
98
  end
95
99
  end
96
100
 
@@ -109,9 +113,11 @@ class Bullet < Graphics::Body
109
113
  w.bullets.delete self if clip
110
114
  end
111
115
 
112
- def draw
113
- w.rect x, y, 2, 2, :white
114
- w.debug "%.2f", m
116
+ class View
117
+ def self.draw w, b
118
+ w.rect b.x, b.y, 2, 2, :white
119
+ w.debug "%.2f", b.m
120
+ end
115
121
  end
116
122
  end
117
123
 
@@ -126,6 +132,9 @@ class TargetSimulation < Graphics::Simulation
126
132
  self.tank = Tank.new self
127
133
  self.bullets = []
128
134
 
135
+ register_body tank
136
+ register_bodies bullets
137
+
129
138
  self.body_img = image "resources/images/body.png"
130
139
  self.turret_img = image "resources/images/turret.png"
131
140
  end
@@ -142,16 +151,9 @@ class TargetSimulation < Graphics::Simulation
142
151
  add_key_handler(:SPACE) { tank.fire }
143
152
  end
144
153
 
145
- def update n
146
- tank.update
147
-
148
- bullets.each(&:update)
149
- end
150
-
151
154
  def draw n
152
- clear
153
- tank.draw
154
- bullets.each(&:draw)
155
+ super
156
+
155
157
  fps n
156
158
  end
157
159
  end
@@ -69,6 +69,17 @@ class Tank
69
69
 
70
70
  def accelerate; self.speed += ACCELERATE; end
71
71
  def decelerate; self.speed -= DECELERATE; end
72
+
73
+ class View
74
+ def self.draw w, b
75
+ x, y, a, t = b.x, b.y, b.angle, b.turret
76
+
77
+ w.blit w.body_img, x, y, a
78
+ w.blit w.turret_img, x, y, t
79
+
80
+ w.debug "%3d @ %.2f @ %d", a, b.speed, b.energy
81
+ end
82
+ end
72
83
  end
73
84
 
74
85
  class Bullet
@@ -87,6 +98,12 @@ class Bullet
87
98
  self.x += Math.cos(rad) * v
88
99
  self.y += Math.sin(rad) * v
89
100
  end
101
+
102
+ class View
103
+ def self.draw w, b
104
+ w.rect b.x, b.y, 2, 2, :white
105
+ end
106
+ end
90
107
  end
91
108
 
92
109
  class TargetSimulation < Graphics::Simulation
@@ -100,6 +117,9 @@ class TargetSimulation < Graphics::Simulation
100
117
  self.tank = Tank.new w/2, h/2
101
118
  self.bullets = []
102
119
 
120
+ register_body tank
121
+ register_bodies bullets
122
+
103
123
  self.body_img = sprite 40, 30 do
104
124
  rect 0, 0, 39, 29, :white
105
125
  rect 0, 4, 39, 21, :white
@@ -119,6 +139,8 @@ class TargetSimulation < Graphics::Simulation
119
139
  def initialize_keys
120
140
  super
121
141
 
142
+ keydown_handler.delete "q" # HACK
143
+
122
144
  add_key_handler(:RIGHT) { tank.turn_right }
123
145
  add_key_handler(:LEFT) { tank.turn_left }
124
146
  add_key_handler(:UP) { tank.accelerate }
@@ -134,34 +156,16 @@ class TargetSimulation < Graphics::Simulation
134
156
  end
135
157
 
136
158
  def update n
137
- tank.update
138
-
139
- bullets.each(&:update)
159
+ super
140
160
 
141
161
  if tank.x < 0 then tank.x = 0 elsif tank.x > w then tank.x = w end
142
162
  if tank.y < 0 then tank.y = 0 elsif tank.y > h then tank.y = h end
143
163
  end
144
164
 
145
165
  def draw n
146
- clear
147
- draw_tank
148
- draw_bullets
149
- fps n
150
- end
151
-
152
- def draw_tank
153
- x, y, a, t = tank.x, tank.y, tank.angle, tank.turret
154
-
155
- blit body_img, x, y, a
156
- blit turret_img, x, y, t
157
-
158
- debug "%3d @ %.2f @ %d", tank.angle, tank.speed, tank.energy
159
- end
166
+ super
160
167
 
161
- def draw_bullets
162
- bullets.each do |b|
163
- rect b.x, b.y, 2, 2, :white
164
- end
168
+ fps n
165
169
  end
166
170
  end
167
171
 
@@ -3,6 +3,8 @@
3
3
  require "graphics"
4
4
 
5
5
  class TargetSimulation < Graphics::Simulation
6
+ CLEAR_COLOR = :darker_green
7
+
6
8
  attr_accessor :bombs
7
9
 
8
10
  def initialize
@@ -20,7 +22,7 @@ class TargetSimulation < Graphics::Simulation
20
22
  end
21
23
 
22
24
  def draw n
23
- clear :darker_green
25
+ clear
24
26
 
25
27
  bombs.each do |(birth, bx, by)|
26
28
  r = n - birth
@@ -1,8 +1,6 @@
1
1
  #!/usr/local/bin/ruby -w
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
- srand 42
5
-
6
4
  require "graphics"
7
5
 
8
6
  ##
@@ -23,7 +21,7 @@ class Vant < Graphics::Body
23
21
  self.black = w.color[:black]
24
22
  end
25
23
 
26
- def forward
24
+ def update
27
25
  move_by a, M
28
26
  mutate
29
27
  end
@@ -39,30 +37,16 @@ class Vant < Graphics::Body
39
37
  end
40
38
  end
41
39
 
42
- class Vants < Graphics::Simulation
40
+ class Vants < Graphics::Drawing
43
41
  attr_accessor :vs
44
42
 
43
+ CLEAR_COLOR = :white
44
+
45
45
  def initialize
46
46
  super 850, 850, 16, self.class.name
47
47
 
48
- # cheat and reopen screen w/o double buffering
49
- self.screen = SDL::Screen.open 850, 850, 16, SDL::HWSURFACE
50
- clear :white
51
-
52
48
  self.vs = populate Vant
53
- end
54
-
55
- def update n
56
- vs.each(&:forward)
57
- end
58
-
59
- def draw_and_flip n
60
- self.draw n
61
- # no flip
62
- end
63
-
64
- def draw n
65
- screen.update 0, 0, 0, 0
49
+ register_bodies vs
66
50
  end
67
51
  end
68
52