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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.autotest +26 -0
- data/.gemtest +0 -0
- data/History.rdoc +12 -0
- data/Manifest.txt +37 -0
- data/README.rdoc +71 -0
- data/Rakefile +26 -0
- data/examples/boid.rb +653 -0
- data/examples/bounce.rb +86 -0
- data/examples/collision.rb +74 -0
- data/examples/demo.rb +90 -0
- data/examples/editor.rb +70 -0
- data/examples/fluid.rb +246 -0
- data/examples/fluid2.rb +199 -0
- data/examples/lito.rb +108 -0
- data/examples/lito2.rb +110 -0
- data/examples/logo.rb +73 -0
- data/examples/math.rb +42 -0
- data/examples/radar.rb +31 -0
- data/examples/tank.rb +160 -0
- data/examples/tank2.rb +173 -0
- data/examples/targeting.rb +46 -0
- data/examples/vants.rb +69 -0
- data/examples/walker.rb +116 -0
- data/examples/zenspider1.rb +93 -0
- data/examples/zenspider2.rb +123 -0
- data/examples/zenspider3.rb +104 -0
- data/examples/zenspider4.rb +90 -0
- data/examples/zombies.rb +385 -0
- data/lib/graphics.rb +9 -0
- data/lib/graphics/body.rb +216 -0
- data/lib/graphics/extensions.rb +48 -0
- data/lib/graphics/simulation.rb +377 -0
- data/lib/graphics/trail.rb +69 -0
- data/lib/graphics/v.rb +71 -0
- data/resources/images/body.png +0 -0
- data/resources/images/turret.png +0 -0
- data/rubysdl_setup.sh +34 -0
- data/test/test_graphics.rb +408 -0
- metadata +191 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
srand 42
|
4
|
+
|
5
|
+
require "graphics"
|
6
|
+
|
7
|
+
class Array
|
8
|
+
def sorted_include? o
|
9
|
+
a, b = o
|
10
|
+
!!bsearch { |(x, y)|
|
11
|
+
c = a - x
|
12
|
+
c.zero? ? b - y : c
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ZenspiderGol
|
18
|
+
delta = [-1, 0, 1]
|
19
|
+
same = [0, 0]
|
20
|
+
|
21
|
+
DELTAS = (delta.product(delta) - [same]).sort
|
22
|
+
MIN = { true => 2, false => 3 }
|
23
|
+
|
24
|
+
@@neighbors = Hash.new { |h, k| h[k] = {} }
|
25
|
+
|
26
|
+
attr_accessor :cells
|
27
|
+
attr_accessor :cache
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
self.cells = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def randomize n, pct
|
34
|
+
m = ((n*n) * pct).to_i
|
35
|
+
dimensions = n.times.to_a
|
36
|
+
cells.replace dimensions.product(dimensions).sample(m).sort
|
37
|
+
end
|
38
|
+
|
39
|
+
def update
|
40
|
+
cells.replace considered.select { |(x, y)| alive? x, y }.sort
|
41
|
+
end
|
42
|
+
|
43
|
+
def considered
|
44
|
+
cells.map { |(x, y)| neighbors_for(x, y) }.flatten(1).uniq
|
45
|
+
end
|
46
|
+
|
47
|
+
def alive? x, y
|
48
|
+
count = (neighbors_for(x, y) & cells).size
|
49
|
+
min = MIN[cells.sorted_include? [x, y]]
|
50
|
+
count.between? min, 3
|
51
|
+
end
|
52
|
+
|
53
|
+
def neighbors_for x, y
|
54
|
+
@@neighbors[x][y] ||=
|
55
|
+
DELTAS.map { |(dx, dy)| [x+dx, y+dy] }.reject { |(m, n)| m < 0 || n < 0 }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ZenspiderGolSimulation < Graphics::Simulation
|
60
|
+
attr_accessor :gol
|
61
|
+
|
62
|
+
SIZE, WIDTH = 10, 64
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
super 640, 640, 16, "Conway's Game of Life"
|
66
|
+
|
67
|
+
self.gol = ZenspiderGol.new
|
68
|
+
gol.randomize WIDTH, 0.15
|
69
|
+
end
|
70
|
+
|
71
|
+
def draw n
|
72
|
+
clear
|
73
|
+
|
74
|
+
gol.cells.each do |(x, y)|
|
75
|
+
ellipse x*SIZE, y*SIZE, (SIZE-1)/2, (SIZE-1)/2, :white, :filled
|
76
|
+
end
|
77
|
+
|
78
|
+
fps n
|
79
|
+
end
|
80
|
+
|
81
|
+
def update n
|
82
|
+
gol.update.reject! { |(x, y)| x >= WIDTH || y >= WIDTH }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if ARGV.first == "prof" then
|
87
|
+
ZenspiderGolSimulation.new.run 5
|
88
|
+
else
|
89
|
+
ZenspiderGolSimulation.new.run
|
90
|
+
end
|
data/examples/zombies.rb
ADDED
@@ -0,0 +1,385 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
srand 42
|
4
|
+
|
5
|
+
require "graphics"
|
6
|
+
|
7
|
+
class Entity
|
8
|
+
attr_accessor :x, :y
|
9
|
+
attr_accessor :speed
|
10
|
+
attr_accessor :sim
|
11
|
+
|
12
|
+
@@colors = false
|
13
|
+
|
14
|
+
def initialize sim
|
15
|
+
self.sim = sim
|
16
|
+
self.speed = 5
|
17
|
+
|
18
|
+
self.x = rand(sim.w / sim.scale)
|
19
|
+
self.y = rand(sim.h / sim.scale)
|
20
|
+
end
|
21
|
+
|
22
|
+
def distance_from_squared p
|
23
|
+
dx = p.x - x
|
24
|
+
dy = p.y - y
|
25
|
+
dx * dx + dy * dy
|
26
|
+
end
|
27
|
+
|
28
|
+
VISIBILITY = 16
|
29
|
+
VIS_SQ = VISIBILITY * VISIBILITY
|
30
|
+
|
31
|
+
def near? p
|
32
|
+
distance_from_squared(p) < VIS_SQ
|
33
|
+
end
|
34
|
+
|
35
|
+
def touching? p
|
36
|
+
distance_from_squared(p) < 4 # 2 * 2
|
37
|
+
end
|
38
|
+
|
39
|
+
def partition
|
40
|
+
(x / sim.width_of_partition) + sim.side * (y / sim.width_of_partition)
|
41
|
+
end
|
42
|
+
|
43
|
+
def random_walk
|
44
|
+
self.x += rand(speed)-speed/2
|
45
|
+
self.y += rand(speed)-speed/2
|
46
|
+
end
|
47
|
+
|
48
|
+
def max
|
49
|
+
@@max ||= sim.max
|
50
|
+
end
|
51
|
+
|
52
|
+
def limit_bounds
|
53
|
+
self.x = 0 if x < 0
|
54
|
+
self.x = max - 1 if x >= max
|
55
|
+
self.y = 0 if y < 0
|
56
|
+
self.y = max - 1 if y >= max
|
57
|
+
end
|
58
|
+
|
59
|
+
def move_towards entity
|
60
|
+
self.x += (entity.x - x) <=> 0
|
61
|
+
self.y += (entity.y - y) <=> 0
|
62
|
+
limit_bounds
|
63
|
+
end
|
64
|
+
|
65
|
+
def draw
|
66
|
+
raise "subclass responsibility"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Person < Entity
|
71
|
+
COUNT = 750
|
72
|
+
|
73
|
+
NORMAL = 1
|
74
|
+
FREAKD = 2
|
75
|
+
INFECT = 3
|
76
|
+
|
77
|
+
INFECT_STEPS = 50.0 # must be a float
|
78
|
+
|
79
|
+
NORMAL_COLOR = :blue
|
80
|
+
FREAKD_COLOR = :yellow
|
81
|
+
|
82
|
+
attr_accessor :state, :infect, :speed
|
83
|
+
|
84
|
+
def initialize sim, state = NORMAL
|
85
|
+
super(sim)
|
86
|
+
|
87
|
+
self.state = state
|
88
|
+
self.speed = 5
|
89
|
+
self.infect = nil
|
90
|
+
|
91
|
+
initialize_colors unless @@colors
|
92
|
+
end
|
93
|
+
|
94
|
+
def initialize_colors
|
95
|
+
@@colors = true
|
96
|
+
|
97
|
+
INFECT_STEPS.to_i.times do |n|
|
98
|
+
r = (255 * ((INFECT_STEPS - n) / INFECT_STEPS)).to_i
|
99
|
+
g = (192 * (n / INFECT_STEPS)).to_i
|
100
|
+
b = 0
|
101
|
+
sim.register_color "infect#{n}", r, g, b
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def infected?
|
106
|
+
state == INFECT
|
107
|
+
end
|
108
|
+
|
109
|
+
def freaked?
|
110
|
+
state == FREAKD
|
111
|
+
end
|
112
|
+
|
113
|
+
def color
|
114
|
+
case state
|
115
|
+
when NORMAL
|
116
|
+
NORMAL_COLOR
|
117
|
+
when FREAKD
|
118
|
+
FREAKD_COLOR
|
119
|
+
when INFECT
|
120
|
+
"infect#{INFECT_STEPS.to_i - infect}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def draw
|
125
|
+
sim.fast_rect x*2, y*2, 2, 2, color
|
126
|
+
end
|
127
|
+
|
128
|
+
def update_infection
|
129
|
+
if @infect then
|
130
|
+
@infect -= 1
|
131
|
+
sim.zombie << Zombie.from_person(sim.person.delete(self), sim) if @infect <= 0
|
132
|
+
true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def visible
|
137
|
+
sim.part_z[partition].find_all { |p| self.near? p }
|
138
|
+
end
|
139
|
+
|
140
|
+
def nearest
|
141
|
+
visible.sort_by { |p| self.distance_from_squared p }.first
|
142
|
+
end
|
143
|
+
|
144
|
+
def update i
|
145
|
+
return if update_infection
|
146
|
+
random_walk
|
147
|
+
limit_bounds
|
148
|
+
|
149
|
+
nearest = self.nearest
|
150
|
+
|
151
|
+
unless nearest then
|
152
|
+
if state == FREAKD then
|
153
|
+
self.state = NORMAL
|
154
|
+
self.speed = 5
|
155
|
+
end
|
156
|
+
else
|
157
|
+
unless touching? nearest then
|
158
|
+
if state == NORMAL then
|
159
|
+
self.state = FREAKD
|
160
|
+
self.speed = 9
|
161
|
+
end
|
162
|
+
else
|
163
|
+
self.state = INFECT
|
164
|
+
self.infect = INFECT_STEPS.to_i
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def kill
|
170
|
+
sim.person.delete self
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Hunter < Person
|
175
|
+
COUNT = 6
|
176
|
+
COLOR = :white
|
177
|
+
|
178
|
+
def color
|
179
|
+
if @infect then
|
180
|
+
"infect#{INFECT_STEPS.to_i - infect}"
|
181
|
+
else
|
182
|
+
COLOR
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def update i
|
187
|
+
return if update_infection
|
188
|
+
random_walk
|
189
|
+
limit_bounds
|
190
|
+
|
191
|
+
baddies = sim.zombie + sim.person.select(&:infect)
|
192
|
+
nearest = baddies.sort_by { |z| self.distance_from_squared z }.first
|
193
|
+
if nearest then
|
194
|
+
if self.touching? nearest then
|
195
|
+
if Person === nearest then
|
196
|
+
nearest.kill
|
197
|
+
else
|
198
|
+
if rand(10) != 0 then
|
199
|
+
nearest.kill
|
200
|
+
else
|
201
|
+
self.state = INFECT
|
202
|
+
self.infect = INFECT_STEPS.to_i
|
203
|
+
end
|
204
|
+
end
|
205
|
+
elsif near? nearest then
|
206
|
+
move_towards nearest
|
207
|
+
else
|
208
|
+
move_towards nearest
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def draw
|
214
|
+
sim.circle x*2, y*2, VISIBILITY, color
|
215
|
+
super
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
class Zombie < Entity
|
220
|
+
COUNT = 5
|
221
|
+
ZOMBIE_COLOR = :red
|
222
|
+
|
223
|
+
def self.from_person p, sim
|
224
|
+
z = new sim
|
225
|
+
z.x = p.x
|
226
|
+
z.y = p.y
|
227
|
+
z
|
228
|
+
end
|
229
|
+
|
230
|
+
def initialize sim
|
231
|
+
super
|
232
|
+
self.speed = 3
|
233
|
+
end
|
234
|
+
|
235
|
+
def draw
|
236
|
+
sim.fast_rect x*2, y*2, 2, 2, ZOMBIE_COLOR
|
237
|
+
end
|
238
|
+
|
239
|
+
def visible
|
240
|
+
sim.part_p[partition].find_all { |p| Hunter === p || p.freaked? }
|
241
|
+
end
|
242
|
+
|
243
|
+
def nearest
|
244
|
+
visible.sort_by { |p| self.distance_from_squared p }.first
|
245
|
+
end
|
246
|
+
|
247
|
+
def update i
|
248
|
+
nearest = self.nearest
|
249
|
+
|
250
|
+
if nearest then
|
251
|
+
move_towards nearest
|
252
|
+
else
|
253
|
+
random_walk
|
254
|
+
end
|
255
|
+
|
256
|
+
limit_bounds
|
257
|
+
end
|
258
|
+
|
259
|
+
def kill
|
260
|
+
sim.zombie.delete self
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class ZombieGame < Graphics::Simulation
|
265
|
+
attr_accessor :person, :zombie
|
266
|
+
attr_accessor :part_p, :part_z
|
267
|
+
attr_accessor :scale, :partitions
|
268
|
+
attr_accessor :start
|
269
|
+
|
270
|
+
def initialize
|
271
|
+
super 512, 512, 16, "Zombie Epidemic Simulator"
|
272
|
+
self.scale = 2
|
273
|
+
self.partitions = 64
|
274
|
+
|
275
|
+
self.part_p = Array.new(partitions) do [] end
|
276
|
+
self.part_z = Array.new(partitions) do [] end
|
277
|
+
|
278
|
+
self.person = []
|
279
|
+
self.zombie = []
|
280
|
+
|
281
|
+
populate Person, person
|
282
|
+
populate Zombie, zombie
|
283
|
+
populate Hunter, person
|
284
|
+
|
285
|
+
self.start = Time.now
|
286
|
+
end
|
287
|
+
|
288
|
+
def draw tick
|
289
|
+
clear
|
290
|
+
|
291
|
+
person.each do |p|
|
292
|
+
p.draw
|
293
|
+
end
|
294
|
+
|
295
|
+
zombie.each do |p|
|
296
|
+
p.draw
|
297
|
+
end
|
298
|
+
|
299
|
+
fps tick
|
300
|
+
end
|
301
|
+
|
302
|
+
def update i
|
303
|
+
partition_into person, part_p, partitions, side
|
304
|
+
partition_into zombie, part_z, partitions, side
|
305
|
+
|
306
|
+
person.each do |p|
|
307
|
+
p.update i
|
308
|
+
end
|
309
|
+
|
310
|
+
zombie.each do |p|
|
311
|
+
p.update i
|
312
|
+
end
|
313
|
+
|
314
|
+
if zombie.empty? or person.all?(&:infected?) then
|
315
|
+
t = Time.now - start
|
316
|
+
if zombie.empty? then
|
317
|
+
print "Infestation stopped "
|
318
|
+
else
|
319
|
+
print "All people infected "
|
320
|
+
end
|
321
|
+
puts "in #{i} iterations, #{t} sec"
|
322
|
+
puts " #{i / t} frames / sec"
|
323
|
+
|
324
|
+
exit
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def populate klass, coll
|
329
|
+
klass::COUNT.times do
|
330
|
+
coll << klass.new(self)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# TODO: rename
|
335
|
+
def side
|
336
|
+
@side ||= Math.sqrt(partitions).to_i
|
337
|
+
end
|
338
|
+
|
339
|
+
def partition_into from, to, size, side
|
340
|
+
to.each(&:clear)
|
341
|
+
|
342
|
+
from.each do |p|
|
343
|
+
part = p.partition
|
344
|
+
|
345
|
+
# -3 or less - do nothing
|
346
|
+
# -2 - add 0
|
347
|
+
# -1 - add 0, 1
|
348
|
+
# 0 or more - add idx..idx+2
|
349
|
+
|
350
|
+
idx = part - side - 1
|
351
|
+
if idx >= 0 then
|
352
|
+
to[idx] << p
|
353
|
+
to[idx+1] << p
|
354
|
+
to[idx+2] << p
|
355
|
+
else
|
356
|
+
to[0] << p if idx >= -2
|
357
|
+
to[1] << p if idx >= -1
|
358
|
+
end
|
359
|
+
|
360
|
+
idx = part - 1
|
361
|
+
to[idx] << p if idx >= 0
|
362
|
+
idx += 1
|
363
|
+
to[idx] << p
|
364
|
+
idx += 1
|
365
|
+
to[idx] << p if idx < size
|
366
|
+
|
367
|
+
idx = part + side - 1
|
368
|
+
to[idx] << p if idx < size
|
369
|
+
idx += 1
|
370
|
+
to[idx] << p if idx < size
|
371
|
+
idx += 1
|
372
|
+
to[idx] << p if idx < size
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def width_of_partition
|
377
|
+
@width_of_partition ||= (w / scale) / side
|
378
|
+
end
|
379
|
+
|
380
|
+
def max
|
381
|
+
@max ||= w / scale
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
ZombieGame.new.run
|