arbol 0.0.2

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.
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