arbol 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/bin/arbol +25 -0
  3. data/lib/arbol.rb +45 -0
  4. data/lib/base.rb +397 -0
  5. data/lib/builder.rb +102 -0
  6. data/lib/documentation.rb +19 -0
  7. data/lib/dsl.rb +168 -0
  8. data/lib/functions/add.rb +70 -0
  9. data/lib/functions/add_constrain.rb +70 -0
  10. data/lib/functions/add_modulo.rb +70 -0
  11. data/lib/functions/analog_pin.rb +316 -0
  12. data/lib/functions/choose.rb +74 -0
  13. data/lib/functions/const.rb +63 -0
  14. data/lib/functions/create_lookup.rb +63 -0
  15. data/lib/functions/create_ref.rb +48 -0
  16. data/lib/functions/crossfade.rb +73 -0
  17. data/lib/functions/divide.rb +70 -0
  18. data/lib/functions/feedback.rb +65 -0
  19. data/lib/functions/feedback_offset.rb +69 -0
  20. data/lib/functions/gamma.rb +61 -0
  21. data/lib/functions/greater_than.rb +70 -0
  22. data/lib/functions/greater_than_equals.rb +70 -0
  23. data/lib/functions/lamp_phase.rb +39 -0
  24. data/lib/functions/less_than.rb +70 -0
  25. data/lib/functions/less_than_equals.rb +70 -0
  26. data/lib/functions/lfo_square.rb +68 -0
  27. data/lib/functions/lfo_triangle.rb +72 -0
  28. data/lib/functions/lookup.rb +86 -0
  29. data/lib/functions/max.rb +70 -0
  30. data/lib/functions/min.rb +70 -0
  31. data/lib/functions/minus.rb +70 -0
  32. data/lib/functions/modulo.rb +70 -0
  33. data/lib/functions/noise.rb +49 -0
  34. data/lib/functions/noise_pixel.rb +49 -0
  35. data/lib/functions/phasor.rb +96 -0
  36. data/lib/functions/ref.rb +34 -0
  37. data/lib/functions/scale.rb +71 -0
  38. data/lib/functions/table.rb +16 -0
  39. data/lib/functions/times.rb +70 -0
  40. data/lib/functions/triangle.rb +69 -0
  41. data/lib/templates/arduino_library.ino.erb +75 -0
  42. metadata +84 -0
@@ -0,0 +1,102 @@
1
+ require 'json'
2
+ require 'pp'
3
+ require 'erb'
4
+
5
+ require_relative 'base.rb'
6
+
7
+ # module used to handle the global class map
8
+ module Arbol
9
+ @@class_map = {}
10
+
11
+ def self.class_map
12
+ @@class_map
13
+ end
14
+
15
+ def self.add_mapped_class(type, mapped_class, code)
16
+ @@class_map[type] = mapped_class
17
+ @@libs << code if code
18
+ end
19
+
20
+ @@libs = []
21
+
22
+ def self.libs
23
+ @@libs
24
+ end
25
+ end
26
+
27
+ # new instance of the class specified by params[:type]
28
+ def builder(params)
29
+ # puts params
30
+ Arbol.class_map[params[:type]].new(params)
31
+ end
32
+
33
+ def custom_arduino_script_body(structure)
34
+ ref_builders = []
35
+ structure[:refs].each do |ref|
36
+ this_ref = builder(
37
+ ref
38
+ )
39
+ this_ref.resolve_frame_optimized
40
+ ref_builders << this_ref
41
+ end
42
+
43
+ # run the builder
44
+ t = builder(
45
+ structure[:calc]
46
+ )
47
+
48
+ t.resolve_frame_optimized
49
+
50
+ # create a tsortable hash and populate it with the nodes
51
+ ts = TsortableHash.new
52
+
53
+ # first append the ref nodes
54
+ ref_builders.each { |r| r.append_tsortable(ts) }
55
+
56
+ # then append the primary calc nodes
57
+ t.append_tsortable(ts)
58
+
59
+ # creates a hash containing all the code keyed by node name
60
+ pixel_scope_code = {}
61
+
62
+ # creates an hash of code for top_level_scope declarations
63
+ top_level_scope_code = {}
64
+
65
+ # contains cycle level code
66
+ cycle_scope_code = {}
67
+
68
+ # first append the ref nodes
69
+ ref_builders.each { |r| r.add_arduino_code(pixel_scope_code) }
70
+ ref_builders.each { |r| r.add_cycle_level_scope(cycle_scope_code) }
71
+ ref_builders.each { |r| r.add_top_level_scope(top_level_scope_code) }
72
+
73
+ # then the primary calc nodes
74
+ t.add_arduino_code(pixel_scope_code)
75
+ t.add_cycle_level_scope(cycle_scope_code)
76
+ t.add_top_level_scope(top_level_scope_code)
77
+
78
+ pixel_scope = []
79
+ top_level_scope = []
80
+ cycle_scope = []
81
+ # run tsort... then append the lines of code in the order they should be executed
82
+ t = ts.tsort
83
+ t.each do |func|
84
+ pixel_scope_code[func].each do |stmt|
85
+ pixel_scope << stmt
86
+ end
87
+
88
+ cycle_scope_code[func].each do |stmt|
89
+ cycle_scope << stmt
90
+ end
91
+
92
+ top_level_scope_code[func].each do |stmt|
93
+ top_level_scope << stmt
94
+ end
95
+ end
96
+
97
+ # last output needs to be passed to the strip
98
+ pixel_scope << ''
99
+ pixel_scope << "// output"
100
+ pixel_scope << "strip.setPixelColor(i, (byte)long_mult(255, #{t.last}[0]), (byte)long_mult(255, #{t.last}[1]), (byte)long_mult(255, #{t.last}[2]));"
101
+ return top_level_scope, cycle_scope, pixel_scope
102
+ end
@@ -0,0 +1,19 @@
1
+ module Arbol
2
+ class Documentation
3
+ def output_all_documentation
4
+ (self.public_methods - Object.public_methods).sort.each do |m|
5
+ unless [:output_all_documentation, :output_single_doc].include? m.to_sym
6
+ output_single_doc(m)
7
+ end
8
+ end
9
+ end
10
+
11
+ def output_single_doc(func)
12
+ puts(
13
+ self.send(
14
+ func.to_sym
15
+ )
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,168 @@
1
+ # scales float values to the integer scale representation.
2
+ # @param [Float|Integer] val
3
+ # @return [Integer]
4
+ def scale_correctly(val)
5
+ case
6
+ when val.class == Integer then return val
7
+ when val.class == Float then return (val * 8192).to_i
8
+ end
9
+ end
10
+
11
+ # resolves an input object to a structure appropriate for the tree.
12
+ # @param [Object] val
13
+ # @return [Object]
14
+ def resolve(val)
15
+ case
16
+ when val.class == Integer then return my_const(scale_correctly(val))
17
+ when val.class == Float then return my_const(scale_correctly(val))
18
+ when val.class == Array then return my_const(
19
+ val.map { |i| scale_correctly(i) }
20
+ )
21
+ when val.class == ArbolHash then return val
22
+ end
23
+ end
24
+
25
+ def resolve_lookup_table(val)
26
+ val.map { |v| resolve(val) }
27
+ end
28
+
29
+ def resolve_integer(val)
30
+ if val.class == Integer
31
+ val
32
+ else
33
+ raise "#{val} is not an integer"
34
+ end
35
+ end
36
+
37
+ def resolve_float(val)
38
+ if val.class == Float
39
+ val
40
+ else
41
+ raise "#{val} is not an float"
42
+ end
43
+ end
44
+
45
+ def resolve_positive_scalar(val)
46
+ if [Integer, Float].include?(val.class)
47
+ if val >= 0
48
+ scale_correctly(val)
49
+ else
50
+ raise "#{} val should be positive"
51
+ end
52
+ else
53
+ raise "#{val} must be a positive scalar number"
54
+ end
55
+ end
56
+
57
+ def resolve_pin_reference(val)
58
+ if val.match(/(^A\d+$|^\d+$)/)
59
+ val
60
+ else
61
+ raise "#{val} is not a valid pin reference"
62
+ end
63
+ end
64
+
65
+ # coerces a scalar value to an array of 3 elements.
66
+ # @param [Object] input
67
+ # @return [Array]
68
+ def coerce_array(input)
69
+ if input.class == Array
70
+ input
71
+ else
72
+ [input, input, input]
73
+ end
74
+ end
75
+
76
+ # top level object to create a neopixel strip
77
+ # @param [Integer] lamps
78
+ # @param [Integer] pin
79
+ # @param [ArbolHash] calc
80
+ # @return [Hash]
81
+ def strip(lamps, pin, calc)
82
+ {
83
+ type: 'physical_strip',
84
+ lamps: lamps,
85
+ pin: pin,
86
+ calc: resolve(calc)
87
+ }
88
+ end
89
+
90
+ # removes comments from the dsl script
91
+ # @param [String] script
92
+ # @return [String]
93
+ def remove_comments(script)
94
+ t = []
95
+ script.split("\n").each do |line|
96
+ if line.match(/\#/)
97
+ t << line.split('#',2)[0]
98
+ else
99
+ t << line
100
+ end
101
+ end
102
+ t.join("\n")
103
+ end
104
+
105
+ # separates declarations by ; delimiter
106
+ # @param [String] script
107
+ # @return [Array<String>]
108
+ def script_split(script)
109
+ script.split(';').select { |l| l.strip != '' }
110
+ end
111
+
112
+ # converts an array of statements to an arbol tree structure
113
+ # @param [Array<String>] stmts
114
+ # @param [Binding] scope
115
+ # @param [Hash]
116
+ def stmts_to_structure(stmts, scope)
117
+ # defined references that can be used in the script
118
+ refs = []
119
+ # injection of ruby code into the eval
120
+ # starts with the refinement, but refs will be added
121
+ refinjects = ['using RefineBasics; ']
122
+
123
+ # iterate all of the statements. last statement must be a strip definition
124
+ stmts.each do |stmt|
125
+ # ref handling
126
+ if stmt.match(/(\w|\s)+={1}(\w|\s)+/)
127
+ ref_name = stmt.match(/\w+/)[0]
128
+ statements = "#{refinjects.join('')}#{stmt.split('=', 2)[1]}"
129
+ this_ref = create_ref(
130
+ ref_name,
131
+ eval(statements, scope)
132
+ )
133
+ refinject = "#{ref_name} = ref('#{ref_name}');"
134
+ refs << this_ref
135
+ refinjects << refinject
136
+ # strip handling
137
+ elsif stmt.match('strip')
138
+ statements = "#{refinjects.join('')} #{stmt}"
139
+ retval = eval(statements, scope)
140
+ retval[:refs] = refs
141
+ return retval
142
+ end
143
+ end
144
+ end
145
+
146
+ # converts dsl text to an arbol tree structure
147
+ # @param [String] script_text
148
+ # @param [Binding] scope
149
+ def interpret_dsl(script_text, scope)
150
+ stmts_to_structure(
151
+ script_split(
152
+ remove_comments(
153
+ script_text
154
+ )
155
+ ),
156
+ scope
157
+ )
158
+ end
159
+
160
+ # converts a json string to an arbol tree structure
161
+ # @param [String] json_text
162
+ # @return [Hash]
163
+ def interpret_json(json_text)
164
+ JSON.parse(
165
+ json_text,
166
+ symbolize_names: true
167
+ )
168
+ end
@@ -0,0 +1,70 @@
1
+ class Add < Base
2
+ Arbol.add_mapped_class(
3
+ 'add',
4
+ Add,
5
+ %{void add(long op1[3], long op2[3], long out[3]) {
6
+ out[0] = op1[0] + op2[0];
7
+ out[1] = op1[1] + op2[1];
8
+ out[2] = op1[2] + op2[2];
9
+ }}
10
+ )
11
+
12
+ attr_accessor :op1
13
+ attr_accessor :op2
14
+
15
+ def param_keys
16
+ [:op1, :op2]
17
+ end
18
+
19
+ def arduino_code
20
+ unless @frame_optimized
21
+ [
22
+ "add(#{@op1.name}, #{@op2.name}, #{@name});"
23
+ ]
24
+ else
25
+ []
26
+ end
27
+ end
28
+
29
+ def cycle_level_arduino_code
30
+ if @frame_optimized
31
+ [
32
+ "add(#{@op1.name}, #{@op2.name}, #{@name});"
33
+ ]
34
+ else
35
+ []
36
+ end
37
+ end
38
+
39
+ def top_level_scope_code
40
+ [
41
+ "long #{@name}[3];",
42
+ ]
43
+ end
44
+ end
45
+
46
+ module Arbol
47
+ class Documentation
48
+
49
+ def add
50
+ %{--
51
+ ### add(op1, op2)
52
+
53
+ * **op1** - operator1
54
+ * **op2** - operator2
55
+
56
+ Adds op1 and op2. can also be used in the form `op1 + op2`.
57
+
58
+ }
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ def add(op1, op2)
65
+ h = ArbolHash.new
66
+ h[:type] = 'add'
67
+ h[:op1] = resolve(op1)
68
+ h[:op2] = resolve(op2)
69
+ h
70
+ end
@@ -0,0 +1,70 @@
1
+ class AddConstrain < Base
2
+ Arbol.add_mapped_class(
3
+ 'add_constrain',
4
+ AddConstrain,
5
+ %{void add_constrain(long op1[3], long op2[3], long out[3]) {
6
+ out[0] = constrain(op1[0] + op2[0], 0, INTEGER_SCALE);
7
+ out[1] = constrain(op1[1] + op2[1], 0, INTEGER_SCALE);
8
+ out[2] = constrain(op1[2] + op2[2], 0, INTEGER_SCALE);
9
+ }}
10
+ )
11
+
12
+ attr_accessor :op1
13
+ attr_accessor :op2
14
+
15
+ def param_keys
16
+ [:op1, :op2]
17
+ end
18
+
19
+ def arduino_code
20
+ unless @frame_optimized
21
+ [
22
+ "add_constrain(#{@op1.name}, #{@op2.name}, #{@name});"
23
+ ]
24
+ else
25
+ []
26
+ end
27
+ end
28
+
29
+ def cycle_level_arduino_code
30
+ if @frame_optimized
31
+ [
32
+ "add_constrain(#{@op1.name}, #{@op2.name}, #{@name});"
33
+ ]
34
+ else
35
+ []
36
+ end
37
+ end
38
+
39
+ def top_level_scope_code
40
+ [
41
+ "long #{@name}[3];"
42
+ ]
43
+ end
44
+ end
45
+
46
+ module Arbol
47
+ class Documentation
48
+
49
+ def add_constrain
50
+ %{--
51
+ ### add\_constrain(op1, op2)
52
+
53
+ * **op1** - operator1
54
+ * **op2** - operator2
55
+
56
+ Adds op1 and op2, then constrains the result between 0.0-~1.0.
57
+
58
+ }
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ def add_constrain(op1, op2)
65
+ h = ArbolHash.new
66
+ h[:type] = 'add_constrain'
67
+ h[:op1] = resolve(op1)
68
+ h[:op2] = resolve(op2)
69
+ h
70
+ end
@@ -0,0 +1,70 @@
1
+ class AddModulo < Base
2
+ Arbol.add_mapped_class(
3
+ 'add_modulo',
4
+ AddModulo,
5
+ %{void add_modulo(long op1[3], long op2[3], long out[3]) {
6
+ out[0] = (op1[0] + op2[0]) % INTEGER_SCALE;
7
+ out[1] = (op1[1] + op2[1]) % INTEGER_SCALE;
8
+ out[2] = (op1[2] + op2[2]) % INTEGER_SCALE;
9
+ }}
10
+ )
11
+
12
+ attr_accessor :op1
13
+ attr_accessor :op2
14
+
15
+ def param_keys
16
+ [:op1, :op2]
17
+ end
18
+
19
+ def arduino_code
20
+ unless @frame_optimized
21
+ [
22
+ "add_modulo(#{@op1.name}, #{@op2.name}, #{@name});"
23
+ ]
24
+ else
25
+ []
26
+ end
27
+ end
28
+
29
+ def cycle_level_arduino_code
30
+ if @frame_optimized
31
+ [
32
+ "add_modulo(#{@op1.name}, #{@op2.name}, #{@name});"
33
+ ]
34
+ else
35
+ []
36
+ end
37
+ end
38
+
39
+ def top_level_scope_code
40
+ [
41
+ "long #{@name}[3];",
42
+ ]
43
+ end
44
+ end
45
+
46
+ module Arbol
47
+ class Documentation
48
+
49
+ def add_modulo
50
+ %{--
51
+ ### add\_modulo(op1, op2)
52
+
53
+ * **op1** - operator1
54
+ * **op2** - operator2
55
+
56
+ Adds op1 and op2, then returns the result modulo 1.0.
57
+
58
+ }
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ def add_modulo(op1, op2)
65
+ h = ArbolHash.new
66
+ h[:type] = 'add_modulo'
67
+ h[:op1] = resolve(op1)
68
+ h[:op2] = resolve(op2)
69
+ h
70
+ end