wads 0.1.3 → 0.2.3
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
- data/CHANGELOG.md +11 -0
- data/README.md +11 -0
- data/data/sample_graph.csv +11 -0
- data/lib/wads/app.rb +53 -329
- data/lib/wads/data_structures.rb +236 -19
- data/lib/wads/textinput.rb +20 -14
- data/lib/wads/version.rb +1 -1
- data/lib/wads/widgets.rb +2158 -216
- data/lib/wads.rb +1 -1
- data/media/CircleAlpha.png +0 -0
- data/media/CircleAqua.png +0 -0
- data/media/CircleBlue.png +0 -0
- data/media/CircleGray.png +0 -0
- data/media/CircleGreen.png +0 -0
- data/media/CirclePurple.png +0 -0
- data/media/CircleRed.png +0 -0
- data/media/CircleWhite.png +0 -0
- data/media/CircleYellow.png +0 -0
- data/media/GreenLight.png +0 -0
- data/media/RedLight.png +0 -0
- data/media/SampleGraph.png +0 -0
- data/media/WadsScreenshot.png +0 -0
- data/media/YellowLight.png +0 -0
- data/run-graph +3 -0
- data/run-star-wars +3 -0
- data/run-stocks +3 -0
- data/run-theme-test +3 -0
- data/samples/basic_gosu_with_graph_widget.rb +66 -0
- data/samples/gosu_bouncing_ball.rb +34 -0
- data/samples/gosu_hello_world.rb +16 -0
- data/samples/gosu_reaction_time.rb +121 -0
- data/samples/graph.rb +72 -0
- data/samples/star_wars.rb +112 -0
- data/samples/stocks.rb +126 -0
- data/samples/theme_test.rb +256 -0
- data/samples/wads_reaction_time.rb +117 -0
- data/wads.gemspec +1 -1
- metadata +31 -7
- data/run-sample-app +0 -3
- data/sample_app.rb +0 -64
data/lib/wads/data_structures.rb
CHANGED
@@ -4,6 +4,7 @@ module Wads
|
|
4
4
|
|
5
5
|
SPACER = " "
|
6
6
|
VALUE_WIDTH = 10
|
7
|
+
COLOR_TAG = "color"
|
7
8
|
|
8
9
|
DEG_0 = 0
|
9
10
|
DEG_45 = Math::PI * 0.25
|
@@ -24,6 +25,9 @@ module Wads
|
|
24
25
|
DEG_292_5 = DEG_270 + DEG_22_5
|
25
26
|
DEG_337_5 = DEG_315 + DEG_22_5
|
26
27
|
|
28
|
+
#
|
29
|
+
# A convenience data structure to store multiple, named sets of key/value pairs
|
30
|
+
#
|
27
31
|
class HashOfHashes
|
28
32
|
attr_accessor :data
|
29
33
|
|
@@ -31,6 +35,9 @@ module Wads
|
|
31
35
|
@data = {}
|
32
36
|
end
|
33
37
|
|
38
|
+
#
|
39
|
+
# Store the value y based on the key x for the named data set
|
40
|
+
#
|
34
41
|
def set(data_set_name, x, y)
|
35
42
|
data_set = @data[data_set_name]
|
36
43
|
if data_set.nil?
|
@@ -40,6 +47,9 @@ module Wads
|
|
40
47
|
data_set[x] = y
|
41
48
|
end
|
42
49
|
|
50
|
+
#
|
51
|
+
# Retrieve the value for the given key x in the named data set
|
52
|
+
#
|
43
53
|
def get(data_set_name, x)
|
44
54
|
data_set = @data[data_set_name]
|
45
55
|
if data_set.nil?
|
@@ -48,6 +58,9 @@ module Wads
|
|
48
58
|
data_set[x]
|
49
59
|
end
|
50
60
|
|
61
|
+
#
|
62
|
+
# Get the list of keys for the named data set
|
63
|
+
#
|
51
64
|
def keys(data_set_name)
|
52
65
|
data_set = @data[data_set_name]
|
53
66
|
if data_set.nil?
|
@@ -57,6 +70,11 @@ module Wads
|
|
57
70
|
end
|
58
71
|
end
|
59
72
|
|
73
|
+
#
|
74
|
+
# Stats allows you to maintain sets of data values, identified by a key,
|
75
|
+
# or data set name. You can then use Stats methods to get the count, average,
|
76
|
+
# sum, or percentiles for these keys.
|
77
|
+
#
|
60
78
|
class Stats
|
61
79
|
attr_accessor :name
|
62
80
|
attr_accessor :data
|
@@ -250,6 +268,20 @@ module Wads
|
|
250
268
|
end
|
251
269
|
end
|
252
270
|
|
271
|
+
#
|
272
|
+
# A node in a graph data structure. Nodes can be used independently, and you
|
273
|
+
# connect them to other nodes, or you can use an overarching Graph instance
|
274
|
+
# to help manage them. Nodes can carry arbitrary metadata in the tags map.
|
275
|
+
# The children are either other nodes, or an Edge instance which can be used
|
276
|
+
# to add information about the connection. For example, in a map graph use
|
277
|
+
# case, the edge may contain information about the distance between the two
|
278
|
+
# nodes. In other applications, metadata about the edges, or connections,
|
279
|
+
# may not be necessary. This class, and the Graph data structure, support
|
280
|
+
# children in either form. Each child connection is a one-directional
|
281
|
+
# connection. The backlinks are stored and managed internally so that we can
|
282
|
+
# easily navigate between nodes of the graph. Nodes themselves have a name
|
283
|
+
# an an optional value.
|
284
|
+
#
|
253
285
|
class Node
|
254
286
|
attr_accessor :name
|
255
287
|
attr_accessor :value
|
@@ -257,6 +289,7 @@ module Wads
|
|
257
289
|
attr_accessor :outputs
|
258
290
|
attr_accessor :visited
|
259
291
|
attr_accessor :tags
|
292
|
+
attr_accessor :depth
|
260
293
|
|
261
294
|
def id
|
262
295
|
# id is an alias for name
|
@@ -270,6 +303,7 @@ module Wads
|
|
270
303
|
@outputs = []
|
271
304
|
@visited = false
|
272
305
|
@tags = tags
|
306
|
+
@depth = 1
|
273
307
|
end
|
274
308
|
|
275
309
|
def add(name, value = nil, tags = {})
|
@@ -279,6 +313,10 @@ module Wads
|
|
279
313
|
def children
|
280
314
|
@outputs
|
281
315
|
end
|
316
|
+
|
317
|
+
def number_of_links
|
318
|
+
@outputs.size + @backlinks.size
|
319
|
+
end
|
282
320
|
|
283
321
|
def add_child(name, value)
|
284
322
|
add_output(name, value)
|
@@ -371,21 +409,48 @@ module Wads
|
|
371
409
|
end
|
372
410
|
end
|
373
411
|
|
374
|
-
def
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
412
|
+
def bfs(max_depth, &block)
|
413
|
+
node_queue = [self]
|
414
|
+
@depth = 1
|
415
|
+
until node_queue.empty?
|
416
|
+
node = node_queue.shift
|
417
|
+
yield node
|
418
|
+
node.visited = true
|
419
|
+
if node.depth < max_depth
|
420
|
+
# Get the set of all outputs and backlinks
|
421
|
+
h = {}
|
422
|
+
node.outputs.each do |n|
|
423
|
+
if n.is_a? Edge
|
424
|
+
n = n.destination
|
425
|
+
end
|
426
|
+
h[n.name] = n
|
427
|
+
end
|
428
|
+
node.backlinks.each do |n|
|
429
|
+
h[n.name] = n
|
430
|
+
end
|
431
|
+
|
432
|
+
h.values.each do |n|
|
433
|
+
if n.visited
|
434
|
+
# ignore, don't process again
|
435
|
+
else
|
436
|
+
n.visited = true
|
437
|
+
n.depth = node.depth + 1
|
438
|
+
node_queue << n
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
382
442
|
end
|
383
|
-
|
384
|
-
|
385
|
-
|
443
|
+
end
|
444
|
+
|
445
|
+
def to_display
|
446
|
+
"Node #{@name}: #{value} inputs: #{@backlinks.size} outputs: #{@outputs.size}"
|
386
447
|
end
|
387
448
|
end
|
388
449
|
|
450
|
+
#
|
451
|
+
# An Edge is a connection between nodes that stores additional information
|
452
|
+
# as arbitrary tags, or name/value pairs.
|
453
|
+
#
|
389
454
|
class Edge
|
390
455
|
attr_accessor :destination
|
391
456
|
attr_accessor :tags
|
@@ -404,6 +469,14 @@ module Wads
|
|
404
469
|
end
|
405
470
|
end
|
406
471
|
|
472
|
+
#
|
473
|
+
# A Graph helps manage nodes by providing high level methods to
|
474
|
+
# add or connect nodes to the graph. It also maintains a list of
|
475
|
+
# nodes and supports having multiple root nodes, i.e. nodes with
|
476
|
+
# no incoming connections.
|
477
|
+
# This class also supports constructing the graph from data stored
|
478
|
+
# in a file.
|
479
|
+
#
|
407
480
|
class Graph
|
408
481
|
attr_accessor :node_list
|
409
482
|
attr_accessor :node_map
|
@@ -412,8 +485,12 @@ module Wads
|
|
412
485
|
@node_list = []
|
413
486
|
@node_map = {}
|
414
487
|
if root_node
|
415
|
-
root_node.
|
416
|
-
|
488
|
+
if root_node.is_a? Node
|
489
|
+
root_node.visit do |n|
|
490
|
+
add_node(n)
|
491
|
+
end
|
492
|
+
elsif root_node.is_a? String
|
493
|
+
read_graph_from_file(root_node)
|
417
494
|
end
|
418
495
|
end
|
419
496
|
end
|
@@ -463,6 +540,37 @@ module Wads
|
|
463
540
|
source.add_output_edge(target, tags)
|
464
541
|
end
|
465
542
|
|
543
|
+
def node_with_most_connections
|
544
|
+
max_node = nil
|
545
|
+
max = -1
|
546
|
+
@node_list.each do |node|
|
547
|
+
num_links = node.number_of_links
|
548
|
+
if num_links > max
|
549
|
+
max = num_links
|
550
|
+
max_node = node
|
551
|
+
end
|
552
|
+
end
|
553
|
+
max_node
|
554
|
+
end
|
555
|
+
|
556
|
+
def get_number_of_connections_range
|
557
|
+
# Find the min and max
|
558
|
+
min = 1000
|
559
|
+
max = 0
|
560
|
+
@node_list.each do |node|
|
561
|
+
num_links = node.number_of_links
|
562
|
+
if num_links < min
|
563
|
+
min = num_links
|
564
|
+
end
|
565
|
+
if num_links > max
|
566
|
+
max = num_links
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
# Then create the scale
|
571
|
+
DataRange.new(min - 0.1, max + 0.1)
|
572
|
+
end
|
573
|
+
|
466
574
|
def find_node(name)
|
467
575
|
@node_map[name]
|
468
576
|
end
|
@@ -474,6 +582,7 @@ module Wads
|
|
474
582
|
def reset_visited
|
475
583
|
@node_list.each do |node|
|
476
584
|
node.visited = false
|
585
|
+
node.depth = 0
|
477
586
|
end
|
478
587
|
end
|
479
588
|
|
@@ -509,7 +618,7 @@ module Wads
|
|
509
618
|
false
|
510
619
|
end
|
511
620
|
|
512
|
-
def traverse_and_collect_nodes(node, max_depth, current_depth = 1)
|
621
|
+
def traverse_and_collect_nodes(node, max_depth = 0, current_depth = 1)
|
513
622
|
if max_depth > 0
|
514
623
|
if current_depth > max_depth
|
515
624
|
return {}
|
@@ -517,21 +626,25 @@ module Wads
|
|
517
626
|
end
|
518
627
|
map = {}
|
519
628
|
if node.visited
|
629
|
+
if current_depth < node.depth
|
630
|
+
node.depth = current_depth
|
631
|
+
end
|
520
632
|
return {}
|
521
633
|
else
|
522
634
|
map[node.name] = node
|
635
|
+
node.depth = current_depth
|
523
636
|
node.visited = true
|
524
637
|
end
|
525
|
-
node.
|
638
|
+
node.outputs.each do |child|
|
639
|
+
if child.is_a? Edge
|
640
|
+
child = child.destination
|
641
|
+
end
|
526
642
|
map_from_child = traverse_and_collect_nodes(child, max_depth, current_depth + 1)
|
527
643
|
map_from_child.each do |key, value|
|
528
644
|
map[key] = value
|
529
645
|
end
|
530
646
|
end
|
531
|
-
node.
|
532
|
-
if child.is_a? Edge
|
533
|
-
child = child.destination
|
534
|
-
end
|
647
|
+
node.backlinks.each do |child|
|
535
648
|
map_from_child = traverse_and_collect_nodes(child, max_depth, current_depth + 1)
|
536
649
|
map_from_child.each do |key, value|
|
537
650
|
map[key] = value
|
@@ -539,8 +652,76 @@ module Wads
|
|
539
652
|
end
|
540
653
|
map
|
541
654
|
end
|
655
|
+
|
656
|
+
def process_tag_string(tags, tag_string)
|
657
|
+
parts = tag_string.partition("=")
|
658
|
+
tag_name = parts[0]
|
659
|
+
tag_value = parts[2]
|
660
|
+
if tag_name == COLOR_TAG
|
661
|
+
begin
|
662
|
+
value = eval(tag_value)
|
663
|
+
puts "Adding tag #{tag_name} = #{value}"
|
664
|
+
tags[tag_name] = value
|
665
|
+
rescue => e
|
666
|
+
puts "Ignoring tag #{tag_name} = #{tag_value}"
|
667
|
+
end
|
668
|
+
else
|
669
|
+
puts "Adding tag #{tag_name} = #{tag_value}"
|
670
|
+
tags[tag_name] = tag_value
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
# The format is a csv file as follows:
|
675
|
+
# N,name,value --> nodes
|
676
|
+
# C,source,destination --> connections (also called edges)
|
677
|
+
#
|
678
|
+
# Optionally, each line type can be followed by comma-separated tag=value
|
679
|
+
def read_graph_from_file(filename)
|
680
|
+
puts "Read graph data from file #{filename}"
|
681
|
+
File.readlines(filename).each do |line|
|
682
|
+
line = line.chomp # remove the carriage return
|
683
|
+
values = line.split(",")
|
684
|
+
type = values[0]
|
685
|
+
tags = {}
|
686
|
+
if type == "N" or type == "n"
|
687
|
+
name = values[1]
|
688
|
+
if values.size > 2
|
689
|
+
value = values[2]
|
690
|
+
# The second position can be a tag or the node value
|
691
|
+
if value.include? "="
|
692
|
+
process_tag_string(tags, value)
|
693
|
+
value = nil
|
694
|
+
end
|
695
|
+
else
|
696
|
+
value = nil
|
697
|
+
end
|
698
|
+
if values.size > 3
|
699
|
+
values[3..-1].each do |tag_string|
|
700
|
+
process_tag_string(tags, tag_string)
|
701
|
+
end
|
702
|
+
end
|
703
|
+
add(name, value, tags)
|
704
|
+
elsif type == "E" or type == "e" or type == "L" or type == "l" or type == "C" or type == "c"
|
705
|
+
source_name = values[1]
|
706
|
+
destination_name = values[2]
|
707
|
+
if values.size > 3
|
708
|
+
values[3..-1].each do |tag_string|
|
709
|
+
process_tag_string(tags, tag_string)
|
710
|
+
end
|
711
|
+
end
|
712
|
+
connect(source_name, destination_name, tags)
|
713
|
+
else
|
714
|
+
puts "Ignoring line: #{line}"
|
715
|
+
end
|
716
|
+
end
|
717
|
+
end
|
542
718
|
end
|
543
719
|
|
720
|
+
#
|
721
|
+
# An internally used data structure that facilitates walking from the leaf nodes
|
722
|
+
# up to the top of the graph, such that a node is only visited once all of its
|
723
|
+
# descendants have been visited.
|
724
|
+
#
|
544
725
|
class GraphReverseIterator
|
545
726
|
attr_accessor :output
|
546
727
|
def initialize(graph)
|
@@ -566,6 +747,42 @@ module Wads
|
|
566
747
|
end
|
567
748
|
end
|
568
749
|
|
750
|
+
#
|
751
|
+
# A single dimension range, going from min to max.
|
752
|
+
# This class has helper methods to create bins within the given range.
|
753
|
+
#
|
754
|
+
class DataRange
|
755
|
+
attr_accessor :min
|
756
|
+
attr_accessor :max
|
757
|
+
attr_accessor :range
|
758
|
+
|
759
|
+
def initialize(min, max)
|
760
|
+
if min < max
|
761
|
+
@min = min
|
762
|
+
@max = max
|
763
|
+
else
|
764
|
+
@min = max
|
765
|
+
@max = min
|
766
|
+
end
|
767
|
+
@range = @max - @min
|
768
|
+
end
|
769
|
+
|
770
|
+
def bin_max_values(number_of_bins)
|
771
|
+
bin_size = @range / number_of_bins.to_f
|
772
|
+
bins = []
|
773
|
+
bin_start_value = @min
|
774
|
+
number_of_bins.times do
|
775
|
+
bin_start_value = bin_start_value + bin_size
|
776
|
+
bins << bin_start_value
|
777
|
+
end
|
778
|
+
bins
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
#
|
783
|
+
# A two dimensional range used by Plot to determine the visible area
|
784
|
+
# which can be a subset of the total data range(s)
|
785
|
+
#
|
569
786
|
class VisibleRange
|
570
787
|
attr_accessor :left_x
|
571
788
|
attr_accessor :right_x
|
data/lib/wads/textinput.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'gosu'
|
2
2
|
|
3
|
+
module Wads
|
4
|
+
|
3
5
|
class TextField < Gosu::TextInput
|
4
6
|
# Some constants that define our appearance.
|
5
7
|
INACTIVE_COLOR = 0xcc666666
|
@@ -12,10 +14,12 @@ class TextField < Gosu::TextInput
|
|
12
14
|
attr_accessor :base_z
|
13
15
|
attr_accessor :old_value
|
14
16
|
|
15
|
-
def initialize(
|
17
|
+
def initialize(x, y, original_text, default_width)
|
16
18
|
super()
|
17
19
|
|
18
|
-
@
|
20
|
+
@x = x
|
21
|
+
@y = y
|
22
|
+
@font = WadsConfig.instance.current_theme.font
|
19
23
|
@default_width = default_width
|
20
24
|
@base_z = 0
|
21
25
|
self.text = original_text
|
@@ -25,15 +29,15 @@ class TextField < Gosu::TextInput
|
|
25
29
|
def draw
|
26
30
|
# Depending on whether this is the currently selected input or not, change the
|
27
31
|
# background's color.
|
28
|
-
if
|
32
|
+
if WadsConfig.instance.get_window.text_input == self then
|
29
33
|
background_color = ACTIVE_COLOR
|
30
34
|
else
|
31
35
|
background_color = INACTIVE_COLOR
|
32
36
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
+
WadsConfig.instance.get_window.draw_quad(x - PADDING, y - PADDING, background_color,
|
38
|
+
x + width + PADDING, y - PADDING, background_color,
|
39
|
+
x - PADDING, y + height + PADDING, background_color,
|
40
|
+
x + width + PADDING, y + height + PADDING, background_color, @base_z + 9)
|
37
41
|
|
38
42
|
# Calculate the position of the caret and the selection start.
|
39
43
|
pos_x = x + @font.text_width(self.text[0...self.caret_pos])
|
@@ -41,15 +45,15 @@ class TextField < Gosu::TextInput
|
|
41
45
|
|
42
46
|
# Draw the selection background, if any; if not, sel_x and pos_x will be
|
43
47
|
# the same value, making this quad empty.
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
+
WadsConfig.instance.get_window.draw_quad(sel_x, y, SELECTION_COLOR,
|
49
|
+
pos_x, y, SELECTION_COLOR,
|
50
|
+
sel_x, y + height, SELECTION_COLOR,
|
51
|
+
pos_x, y + height, SELECTION_COLOR, @base_z + 10)
|
48
52
|
|
49
53
|
# Draw the caret; again, only if this is the currently selected field.
|
50
|
-
if
|
51
|
-
|
52
|
-
|
54
|
+
if WadsConfig.instance.get_window.text_input == self then
|
55
|
+
WadsConfig.instance.get_window.draw_line(pos_x, y, CARET_COLOR,
|
56
|
+
pos_x, y + height, CARET_COLOR, @base_z + 10)
|
53
57
|
end
|
54
58
|
|
55
59
|
# Finally, draw the text itself!
|
@@ -130,3 +134,5 @@ class TextField < Gosu::TextInput
|
|
130
134
|
# empty base implementation
|
131
135
|
end
|
132
136
|
end
|
137
|
+
|
138
|
+
end # end wads module
|
data/lib/wads/version.rb
CHANGED