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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/History.rdoc +38 -0
- data/Manifest.txt +5 -0
- data/Rakefile +2 -0
- data/examples/boid.rb +14 -15
- data/examples/bounce.rb +9 -11
- data/examples/canvas.rb +3 -1
- data/examples/collision.rb +10 -8
- data/examples/demo.rb +12 -12
- data/examples/fluid2.rb +13 -8
- data/examples/gol2.rb +0 -2
- data/examples/math.rb +3 -1
- data/examples/pi_polygon.rb +145 -0
- data/examples/radar.rb +3 -1
- data/examples/rainbow_fluid.rb +243 -0
- data/examples/tank.rb +19 -17
- data/examples/tank2.rb +25 -21
- data/examples/targeting.rb +3 -1
- data/examples/vants.rb +5 -21
- data/examples/walker.rb +15 -12
- data/examples/walker2.rb +28 -26
- data/examples/zombies.rb +0 -2
- data/ext/sdl/sdl.c +10 -3
- data/ext/sdl/sge/sge_primitives.cpp +11 -0
- data/ext/sdl/sge/sge_primitives.h +3 -0
- data/lib/graphics.rb +2 -1
- data/lib/graphics/body.rb +10 -25
- data/lib/graphics/decorators.rb +21 -0
- data/lib/graphics/rainbows.rb +128 -0
- data/lib/graphics/simulation.rb +139 -15
- data/test/test_graphics.rb +45 -9
- data/test/test_rainbows.rb +95 -0
- metadata +20 -28
- metadata.gz.sig +0 -0
data/examples/radar.rb
CHANGED
@@ -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
|
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
|
data/examples/tank.rb
CHANGED
@@ -55,9 +55,11 @@ class Tank < Graphics::Body
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
93
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
153
|
-
|
154
|
-
bullets.each(&:draw)
|
155
|
+
super
|
156
|
+
|
155
157
|
fps n
|
156
158
|
end
|
157
159
|
end
|
data/examples/tank2.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
data/examples/targeting.rb
CHANGED
@@ -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
|
25
|
+
clear
|
24
26
|
|
25
27
|
bombs.each do |(birth, bx, by)|
|
26
28
|
r = n - birth
|
data/examples/vants.rb
CHANGED
@@ -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
|
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::
|
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
|
-
|
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
|
|