graphics 1.0.0b1

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.
@@ -0,0 +1,86 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require "graphics"
4
+
5
+ class Ball < Graphics::Body
6
+ COUNT = 50
7
+
8
+ G = V[0, -18 / 60.0]
9
+
10
+ attr_accessor :g
11
+
12
+ def initialize w
13
+ super
14
+
15
+ self.a = rand 180
16
+ self.m = rand 100
17
+ self.g = G
18
+ end
19
+
20
+ def update
21
+ fall
22
+ move
23
+ bounce
24
+ end
25
+
26
+ def fall
27
+ self.velocity += g
28
+ end
29
+
30
+ def label
31
+ l = "%.1f %.1f" % dx_dy
32
+ w.text l, x-10, y-40, :white
33
+ end
34
+
35
+ def draw
36
+ # w.angle x, y, a, 3*m, :red
37
+ w.angle x, y, a, 50, :red
38
+ w.circle x, y, 5, :white, :filled
39
+ # label
40
+ end
41
+ end
42
+
43
+ class BounceSimulation < Graphics::Simulation
44
+ attr_accessor :bs
45
+
46
+ def initialize
47
+ super 640, 640, 16, "Bounce"
48
+
49
+ self.bs = populate Ball
50
+ end
51
+
52
+ def update n
53
+ bs.each(&:update)
54
+ end
55
+
56
+ def draw n
57
+ clear
58
+ bs.each(&:draw)
59
+ bs.first.label
60
+ fps n
61
+ end
62
+
63
+ def handle_keys
64
+ super
65
+ randomize if SDL::Key.press? SDL::Key::SPACE
66
+ reverse if SDL::Key.press? SDL::Key::R
67
+ end
68
+
69
+ def randomize
70
+ bs.each do |b|
71
+ b.m = rand(100)
72
+ b.a = rand(180)
73
+ end
74
+ end
75
+
76
+ def reverse
77
+ return if @guard
78
+ @guard = true
79
+ bs.each do |b|
80
+ b.g *= -1
81
+ end
82
+ @guard = false
83
+ end
84
+ end
85
+
86
+ BounceSimulation.new.run
@@ -0,0 +1,74 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require "graphics"
4
+
5
+ class Sprite < Graphics::Body
6
+ COUNT = 8
7
+
8
+ attr_accessor :image
9
+
10
+ def initialize w
11
+ super w
12
+
13
+ self.a = random_angle
14
+ self.m = 5
15
+ end
16
+
17
+ def update
18
+ move
19
+ bounce
20
+ end
21
+
22
+ def collide
23
+ self.a = (a + 180).degrees
24
+ end
25
+
26
+ def draw
27
+ w.blit image, x, y, a
28
+ end
29
+
30
+ def collide_with? other
31
+ w.cmap.collision_check(x, y, w.cmap, other.x, other.y) != nil
32
+ end
33
+ end
34
+
35
+ class Collision < Graphics::Simulation
36
+ attr_accessor :sprites, :cmap, :image
37
+
38
+ def initialize
39
+ super 850, 850, 16, "Collision"
40
+
41
+ self.image = SDL::Surface.load "resources/images/body.png"
42
+ self.cmap = image.make_collision_map
43
+
44
+ self.sprites = populate Sprite do |s|
45
+ s.image = image
46
+ end
47
+ end
48
+
49
+ def inspect
50
+ "<Screen ...>"
51
+ end
52
+
53
+ def detect_collisions(sprites)
54
+ collisions = []
55
+ sprites.combination(2).each do |a, b|
56
+ collisions << a << b if a.collide_with? b
57
+ end
58
+ collisions.uniq
59
+ end
60
+
61
+ def update n
62
+ sprites.each(&:update)
63
+ detect_collisions(sprites).each(&:collide)
64
+ end
65
+
66
+ def draw n
67
+ clear
68
+
69
+ sprites.each(&:draw)
70
+ fps n
71
+ end
72
+ end
73
+
74
+ Collision.new.run
@@ -0,0 +1,90 @@
1
+ #!/usr/local/bin/ruby -w
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require "graphics"
5
+
6
+ class Ball < Graphics::Body
7
+ def initialize w
8
+ super
9
+
10
+ self.x = 50
11
+ self.y = 50
12
+
13
+ self.a = 60
14
+ self.m = 3
15
+ end
16
+
17
+ def update
18
+ move
19
+ wrap
20
+ end
21
+
22
+ def draw n
23
+ a = n % 360
24
+
25
+ w.angle x, y, a, 50, :green
26
+ w.circle x, y, 5, :white, :filled
27
+ end
28
+ end
29
+
30
+ class Demo < Graphics::Simulation
31
+ attr_accessor :ball, :img
32
+
33
+ def initialize
34
+ super 800, 800, 16, "Boid"
35
+ self.ball = Ball.new self
36
+
37
+ r = color[:red]
38
+
39
+ self.img = sprite 10, 10 do
40
+ circle 5, 5, 5, :white, :filled
41
+ end
42
+ end
43
+
44
+ def update n
45
+ ball.update
46
+ end
47
+
48
+ def draw n
49
+ clear
50
+
51
+ line 100, 50, 125, 75, :white
52
+
53
+ hline 100, :white
54
+
55
+ vline 100, :white
56
+
57
+ angle 125, 50, 45, 10, :white
58
+
59
+ fast_rect 150, 50, 10, 10, :white
60
+
61
+ point 175, 50, :green
62
+
63
+ rect 200, 50, 10, 10, :white
64
+
65
+ circle 225, 50, 10, :white
66
+
67
+ ellipse 250, 50, 10, 10, :white
68
+
69
+ bezier 275, 50, 275, 100, 285, 0, 300, 50, :white
70
+
71
+ rect 300, 25, 50, 50, :white
72
+ blit img, 325, 50, 0
73
+
74
+ text "blah", 350, 50, :white
75
+
76
+ x, y, * = mouse
77
+ rect x, y, 150, 50, :white
78
+ text "#{x}/#{y}", x, y, :white
79
+
80
+ debug "debug"
81
+
82
+ ball.draw n
83
+
84
+ fps n
85
+ end
86
+ end
87
+
88
+ if $0 == __FILE__
89
+ Demo.new.run
90
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/local/bin/ruby -w
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require "graphics"
5
+
6
+ class Editor < Graphics::Simulation
7
+ attr_accessor :overlay, :s, :lines
8
+
9
+ alias :overlay? :overlay
10
+
11
+ def initialize
12
+ super 850, 850, 16, self.class.name
13
+
14
+ self.overlay = true
15
+ self.s = ""
16
+ self.lines = []
17
+ end
18
+
19
+ def handle_event e, n
20
+ case e
21
+ when SDL::Event::KeyDown then
22
+ if e.mod & (SDL::Key::MOD_LCTRL | SDL::Key::MOD_RCTRL) != 0 then
23
+ case e.sym.chr
24
+ when "t" then
25
+ self.overlay = ! self.overlay
26
+ end
27
+ else
28
+ c = e.sym.chr rescue ""
29
+ c.upcase! if e.mod & (SDL::Key::MOD_LSHIFT | SDL::Key::MOD_RSHIFT) != 0
30
+ case c
31
+ when "\r" then
32
+ c = "\n"
33
+ lines << s.dup
34
+ s.clear
35
+ return
36
+ when "\b" then
37
+ self.s = s[0..-2]
38
+ return
39
+ end
40
+ s << c
41
+ end
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def draw n
48
+ clear
49
+
50
+ draw_scene
51
+ draw_overlay
52
+ end
53
+
54
+ def draw_scene
55
+ end
56
+
57
+ def draw_overlay
58
+ if overlay? then
59
+ lines.each_with_index do |l, i|
60
+ text l, 10, ((lines.size-i)*font.height), :gray
61
+ end
62
+
63
+ text "> #{s}_", 10, 0, :white
64
+ end
65
+ end
66
+ end
67
+
68
+ if $0 == __FILE__
69
+ Editor.new.run
70
+ end
@@ -0,0 +1,246 @@
1
+ require "graphics"
2
+
3
+ class Float
4
+ ##
5
+ # A floating-point friendly `between?` function that excludes
6
+ # the lower bound.
7
+ # Equivalent to `min < x <= max`
8
+ ##
9
+ def xbetween? min, max
10
+ min < self && self <= max
11
+ end
12
+ end
13
+
14
+ class Particle
15
+ attr_accessor :density, :position, :velocity,
16
+ :pressure_force, :viscosity_force
17
+ def initialize pos
18
+ # Scalars
19
+ @density = 0
20
+
21
+ # Forces
22
+ @position = pos
23
+ @velocity = V::ZERO
24
+ @pressure_force = V::ZERO
25
+ @viscosity_force = V::ZERO
26
+ end
27
+ end
28
+
29
+ class SPH
30
+ ##
31
+ # Constants
32
+ #
33
+
34
+ MASS = 5 # Particle mass
35
+ DENSITY = 1 # Rest density
36
+ GRAVITY = V[0, -0.5]
37
+ H = 1 # Smoothing cutoff- essentially, particle size
38
+ K = 20 # Temperature constant- higher means particle repel more strongly
39
+ ETA = 1 # Viscosity constant- higher for more viscous
40
+
41
+ attr_reader :particles
42
+
43
+ def initialize
44
+ # Instantiate particles!
45
+ @particles = []
46
+ (0..10).each do |x|
47
+ (0..10).each do |y|
48
+ jitter = rand * 0.1
49
+ particles << Particle.new(V[x+1+jitter, y+5])
50
+ end
51
+ end
52
+ end
53
+
54
+ ##
55
+ # A weighting function (kernel) for the contribution of each neighbor
56
+ # to a particle's density. Forms a nice smooth gradient from the center
57
+ # of a particle to H, where it's 0
58
+ #
59
+
60
+ def weight r, h
61
+ len_r = r.magnitude
62
+
63
+ if len_r.xbetween? 0, h
64
+ 315.0 / (64 * Math::PI * h**9) * (h**2 - len_r**2)**3
65
+ else
66
+ 0.0
67
+ end
68
+ end
69
+
70
+ ##
71
+ # Gradient ( that is, V(dx, dy) ) of a weighting function for
72
+ # a particle's pressure. This weight function is spiky (not flat or
73
+ # smooth at x=0) so particles close together repel strongly.
74
+ #
75
+
76
+ def gradient_weight_spiky r, h
77
+ len_r = r.magnitude
78
+
79
+ if len_r.xbetween? 0, h
80
+ r * (45.0 / (Math::PI * h**6 * len_r)) * (h - len_r)**2 * (-1.0)
81
+ else
82
+ V::ZERO
83
+ end
84
+ end
85
+
86
+ ##
87
+ # The laplacian of a weighting function that tends towards infinity when
88
+ # approching 0 (slows down particles moving faster than their neighbors)
89
+ #
90
+
91
+ def laplacian_weight_viscosity r, h
92
+ len_r = r.magnitude
93
+
94
+ if len_r.xbetween? 0, h
95
+ 45.0 / (2 * Math::PI * h**5) * (1 - len_r / h)
96
+ else
97
+ 0.0
98
+ end
99
+ end
100
+
101
+ def clear
102
+ # Clear everything
103
+ particles.each do |particle|
104
+ particle.density = DENSITY
105
+ particle.pressure_force = V::ZERO
106
+ particle.viscosity_force = V::ZERO
107
+ end
108
+ end
109
+
110
+ def calculate_density
111
+ # Calculate fluid density around each particle
112
+ particles.each do |particle|
113
+ particles.each do |neighbor|
114
+
115
+ # If particles are close together, density increases
116
+ distance = particle.position - neighbor.position
117
+
118
+ if distance.magnitude < H # Particles are close enough to matter
119
+ particle.density += MASS * weight(distance, H)
120
+ end
121
+ end
122
+ # p particle.density if particle.density > 2*H
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
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
+ end
220
+
221
+ def update time
222
+ simulation.step DELTA_TIME
223
+ simulation.make_particles_stay_in_bounds @scale
224
+ end
225
+
226
+ def draw time
227
+ clear
228
+
229
+ simulation.particles.each do |particle|
230
+ pos = particle.position * s
231
+ vel = particle.velocity * s
232
+
233
+ # Particles
234
+ circle(pos.x, pos.y, 5, :white)
235
+ circle(pos.x, pos.y, particle.density, :gray)
236
+
237
+ # Velocity vectors
238
+ p2 = pos + vel
239
+ line(pos.x, pos.y, p2.x, p2.y, :red)
240
+
241
+ fps time
242
+ end
243
+ end
244
+ end
245
+
246
+ SimulationWindow.new.run