Top Level Namespace

Defined Under Namespace

Modules: Arbol, RefineBasics Classes: Add, AddConstrain, AddModulo, AnalogPin, ArbolHash, ArbolTable, Base, Choose, Const, CreateLookup, CreateRef, Crossfade, Divide, Feedback, FeedbackOffset, Gamma, GreaterThan, GreaterThanEquals, LFOSquare, LFOTriangle, LampPhase, LessThan, LessThanEquals, Lookup, Maximum, Minimum, Minus, Modulo, Noise, NoisePixel, PhaseTriangle, Phasor, Ref, Scale, Times, TsortableHash

Instance Method Summary collapse

Instance Method Details

#add(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/add.rb', line 64

def add(op1, op2)
  h = ArbolHash.new
  h[:type] = 'add'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#add_constrain(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/add_constrain.rb', line 64

def add_constrain(op1, op2)
  h = ArbolHash.new
  h[:type] = 'add_constrain'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#add_modulo(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/add_modulo.rb', line 64

def add_modulo(op1, op2)
  h = ArbolHash.new
  h[:type] = 'add_modulo'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#analog_pin(pin:, in_lo: 0, in_hi: 1.0, out_lo: 0, out_hi: 1.0, threshold: nil, window: nil, feedback: nil) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/functions/analog_pin.rb', line 240

def analog_pin(
    pin:,
    in_lo: 0,
    in_hi: 1.0,
    out_lo: 0,
    out_hi: 1.0,
    threshold: nil,
    window: nil,
    feedback: nil
  )
  
  h = ArbolHash.new
  h[:type] = 'analog_pin'
  h[:pin] = resolve_pin_reference(pin)

  # set scaling if any of the scale parameters are changed
  h[:scale] = 0
  h[:scale] = 1 if in_lo != 0
  h[:scale] = 1 if in_hi != 1.0
  h[:scale] = 1 if out_lo != 0
  h[:scale] = 1 if out_hi != 1.0

  h[:in_lo] = resolve_positive_scalar(in_lo)
  h[:in_hi] = resolve_positive_scalar(in_hi)
  h[:out_lo] = resolve_positive_scalar(out_lo)
  h[:out_hi] = resolve_positive_scalar(out_hi)
  
  if threshold
    h[:threshold] = resolve_positive_scalar(threshold)
  else
    h[:threshold] = -1
  end
  
  if window
    h[:window] = resolve_float(window)
  else
    h[:window] = 0.0
  end
  
  if feedback
    h[:feedback] = resolve_positive_scalar(feedback)
  else
    h[:feedback] = 0
  end
  puts(h)
  h
end

#builder(params) ⇒ Object

new instance of the class specified by params



28
29
30
31
# File 'lib/builder.rb', line 28

def builder(params)
  # puts params
  Arbol.class_map[params[:type]].new(params)
end

#choose(choice, op1, op2) ⇒ Object



67
68
69
70
71
72
73
74
# File 'lib/functions/choose.rb', line 67

def choose(choice, op1, op2)
  h = ArbolHash.new
  h[:type] = 'choose'
  h[:choice] = resolve(choice)
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#coerce_array(input) ⇒ Array

coerces a scalar value to an array of 3 elements.

Parameters:

  • input (Object)

Returns:

  • (Array)


68
69
70
71
72
73
74
# File 'lib/dsl.rb', line 68

def coerce_array(input)
  if input.class == Array
    input
  else
    [input, input, input]
  end
end

#create_ref(identifier, value) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/functions/create_ref.rb', line 36

def create_ref(identifier, value)
  h = ArbolHash.new
  if value.class == ArbolTable
    h[:type] = 'create_lookup'
    h[:identifier] = identifier
    h[:value] = resolve_lookup(value.table)
  else
    h[:type] = 'create_ref'
    h[:identifier] = identifier
    h[:value] = resolve(value)
  end
  h
end

#create_table_ref(identifier, value) ⇒ Object



57
58
59
60
61
62
63
# File 'lib/functions/create_lookup.rb', line 57

def create_table_ref(identifier, value)
  h = ArbolHash.new
  h[:type] = 'create_lookup'
  h[:identifier] = identifier
  h[:value] = resolve_lookup(value)
  h
end

#crossfade(fader, channel1, channel2) ⇒ Object



66
67
68
69
70
71
72
73
# File 'lib/functions/crossfade.rb', line 66

def crossfade(fader, channel1, channel2)
  h = ArbolHash.new
  h[:type] = 'crossfade'
  h[:fader] = resolve(fader)
  h[:channel1] = resolve(channel1)
  h[:channel2] = resolve(channel2)
  h
end

#custom_arduino_script_body(structure) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/builder.rb', line 33

def custom_arduino_script_body(structure)
  ref_builders = []
  structure[:refs].each do |ref|
    this_ref = builder(
      ref
    )
    this_ref.resolve_frame_optimized
    ref_builders << this_ref
  end

  # run the builder
  t = builder(
    structure[:calc]
  )
  
  t.resolve_frame_optimized
  
  # create a tsortable hash and populate it with the nodes
  ts = TsortableHash.new

  # first append the ref nodes
  ref_builders.each { |r| r.append_tsortable(ts) }

  # then append the primary calc nodes
  t.append_tsortable(ts)

  # creates a hash containing all the code keyed by node name
  pixel_scope_code = {}
  
  # creates an hash of code for top_level_scope declarations
  top_level_scope_code = {}
  
  # contains cycle level code
  cycle_scope_code = {}

  # first append the ref nodes
  ref_builders.each { |r| r.add_arduino_code(pixel_scope_code) }
  ref_builders.each { |r| r.add_cycle_level_scope(cycle_scope_code) }
  ref_builders.each { |r| r.add_top_level_scope(top_level_scope_code) }
   
  # then the primary calc nodes
  t.add_arduino_code(pixel_scope_code)
  t.add_cycle_level_scope(cycle_scope_code)
  t.add_top_level_scope(top_level_scope_code)

  pixel_scope = []
  top_level_scope = []
  cycle_scope = []
  # run tsort... then append the lines of code in the order they should be executed
  t = ts.tsort
  t.each do |func|
    pixel_scope_code[func].each do |stmt|
      pixel_scope << stmt
    end
    
    cycle_scope_code[func].each do |stmt|
      cycle_scope << stmt
    end
    
    top_level_scope_code[func].each do |stmt|
      top_level_scope << stmt
    end
  end

  # last output needs to be passed to the strip
  pixel_scope << ''
  pixel_scope << "// output"
  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]));"
  return top_level_scope, cycle_scope, pixel_scope
end

#divide(numerator, denominator) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/divide.rb', line 64

def divide(numerator, denominator)
  h = ArbolHash.new
  h[:type] = 'add'
  h[:numerator] = resolve(numerator)
  h[:denominator] = resolve(denominator)
  h
end

#feedback(input, feedback) ⇒ Object



59
60
61
62
63
64
65
# File 'lib/functions/feedback.rb', line 59

def feedback(input, feedback)
  h = ArbolHash.new
  h[:type] = 'feedback'
  h[:input] = resolve(input)
  h[:feedback] = resolve(feedback)
  h
end

#feedback_offset(input, feedback, offset) ⇒ Object



62
63
64
65
66
67
68
69
# File 'lib/functions/feedback_offset.rb', line 62

def feedback_offset(input, feedback, offset)
  h = ArbolHash.new
  h[:type] = 'feedback_offset'
  h[:input] = resolve(input)
  h[:feedback] = resolve(feedback)
  h[:offset] = resolve(offset)
  h
end

#gamma(input) ⇒ Object



56
57
58
59
60
61
# File 'lib/functions/gamma.rb', line 56

def gamma(input)
  h = ArbolHash.new
  h[:type] = 'gamma'
  h[:input] = resolve(input)
  h
end

#greater_than(left, right) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/greater_than.rb', line 64

def greater_than(left, right)
  h = ArbolHash.new
  h[:type] = 'greater_than'
  h[:left] = resolve(left)
  h[:right] = resolve(right)
  h
end

#greater_than_equals(left, right) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/greater_than_equals.rb', line 64

def greater_than_equals(left, right)
  h = ArbolHash.new
  h[:type] = 'greater_than_equals'
  h[:left] = resolve(left)
  h[:right] = resolve(right)
  h
end

#ino_from_tree(tree) ⇒ Object

creates an ino file from a tree structure.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/arbol.rb', line 21

def ino_from_tree(tree)
  pp tree
  tls, cycle, body = custom_arduino_script_body(tree)
  # these are resolved inside the ERB
  tls = tls.join("\n")
  cycle = cycle.join("\n")
  body = body.join("\n")
  integer_scale = 8192
  pixels = tree[:lamps]
  pin = tree[:pin]
  code = Arbol.libs.join("\n\n")
  ERB.new(
    IO.read(
      "#{File.dirname(__FILE__)}/templates/arduino_library.ino.erb"
    )
  ).result(binding)
end

#interpret_dsl(script_text, scope) ⇒ Object

converts dsl text to an arbol tree structure

Parameters:

  • script_text (String)
  • scope (Binding)


149
150
151
152
153
154
155
156
157
158
# File 'lib/dsl.rb', line 149

def interpret_dsl(script_text, scope)
  stmts_to_structure(
    script_split(
      remove_comments(
        script_text
      )
    ),
    scope
  )
end

#interpret_file(file_path, scope) ⇒ Object

interprets a file into an arbol tree structure



12
13
14
15
16
17
18
# File 'lib/arbol.rb', line 12

def interpret_file(file_path, scope)
  if file_path.match(/\.rb$/)
    return interpret_dsl(File.read(file_path), scope)
  elsif file_path.match(/\.json$/)
    return interpret_json(File.read(file_path))
  end
end

#interpret_json(json_text) ⇒ Hash

converts a json string to an arbol tree structure

Parameters:

  • json_text (String)

Returns:

  • (Hash)


163
164
165
166
167
168
# File 'lib/dsl.rb', line 163

def interpret_json(json_text)
  JSON.parse(
    json_text,
    symbolize_names: true
  )
end

#lamp_phaseObject



35
36
37
38
39
# File 'lib/functions/lamp_phase.rb', line 35

def lamp_phase
  h = ArbolHash.new
  h[:type] = 'lamp_phase'
  h
end

#less_than(left, right) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/less_than.rb', line 64

def less_than(left, right)
  h = ArbolHash.new
  h[:type] = 'less_than'
  h[:left] = resolve(left)
  h[:right] = resolve(right)
  h
end

#less_than_equals(left, right) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/less_than_equals.rb', line 64

def less_than_equals(left, right)
  h = ArbolHash.new
  h[:type] = 'less_than_equals'
  h[:left] = resolve(left)
  h[:right] = resolve(right)
  h
end

#lfo_square(cycle_ms) ⇒ Object



63
64
65
66
67
68
# File 'lib/functions/lfo_square.rb', line 63

def lfo_square(cycle_ms)
  h = ArbolHash.new
  h[:type] = 'lfo_square'
  h[:cycle_ms] = resolve(cycle_ms)
  h
end

#lfo_triangle(cycle_ms) ⇒ Object



67
68
69
70
71
72
# File 'lib/functions/lfo_triangle.rb', line 67

def lfo_triangle(cycle_ms)
  h = ArbolHash.new
  h[:type] = 'lfo_triangle'
  h[:cycle_ms] = resolve(cycle_ms)
  h
end

#lookup(table, index) ⇒ Object



80
81
82
83
84
85
86
# File 'lib/functions/lookup.rb', line 80

def lookup(table, index)
  {
    type: 'lookup',
    table: resolve_table_reference(table),
    index: resolve(index)
  }
end

#max(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/max.rb', line 64

def max(op1, op2)
  h = ArbolHash.new
  h[:type] = 'max'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#min(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/min.rb', line 64

def min(op1, op2)
  h = ArbolHash.new
  h[:type] = 'min'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#minus(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/minus.rb', line 64

def minus(op1, op2)
  h = ArbolHash.new
  h[:type] = 'minus'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#mod(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/modulo.rb', line 64

def mod(op1, op2)
  h = ArbolHash.new
  h[:type] = 'modulo'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#my_const(value) ⇒ Object

given a slightly different name to avoid



58
59
60
61
62
63
# File 'lib/functions/const.rb', line 58

def my_const(value)
  {
    type: "const",
    value: coerce_array(value)
  }
end

#noiseObject



45
46
47
48
49
# File 'lib/functions/noise.rb', line 45

def noise
  h = ArbolHash.new
  h[:type] = 'noise'
  h
end

#noise_pixelObject



45
46
47
48
49
# File 'lib/functions/noise_pixel.rb', line 45

def noise_pixel
  h = ArbolHash.new
  h[:type] = 'noise_pixel'
  h
end

#phasor(cycles) ⇒ Object



91
92
93
94
95
96
# File 'lib/functions/phasor.rb', line 91

def phasor(cycles)
  h = ArbolHash.new
  h[:type] = 'phasor'
  h[:cycles] = resolve(cycles)
  h
end

#ref(identifier) ⇒ Object



29
30
31
32
33
34
# File 'lib/functions/ref.rb', line 29

def ref(identifier)
  h = ArbolHash.new
  h[:type] = 'ref'
  h[:identifier] = identifier
  h
end

#remove_comments(script) ⇒ String

removes comments from the dsl script

Parameters:

  • script (String)

Returns:

  • (String)


93
94
95
96
97
98
99
100
101
102
103
# File 'lib/dsl.rb', line 93

def remove_comments(script)
  t = []
  script.split("\n").each do |line|
    if line.match(/\#/)
      t << line.split('#',2)[0]
    else
      t << line
    end
  end
  t.join("\n")
end

#resolve(val) ⇒ Object

resolves an input object to a structure appropriate for the tree.

Parameters:

  • val (Object)

Returns:

  • (Object)


14
15
16
17
18
19
20
21
22
23
# File 'lib/dsl.rb', line 14

def resolve(val)
  case
    when val.class == Integer then return my_const(scale_correctly(val))
    when val.class == Float then return my_const(scale_correctly(val))
    when val.class == Array then return my_const(
      val.map { |i| scale_correctly(i) }
    )
    when val.class == ArbolHash then return val
  end
end

#resolve_float(val) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/dsl.rb', line 37

def resolve_float(val)
  if val.class == Float
    val
  else
    raise "#{val} is not an float"
  end
end

#resolve_integer(val) ⇒ Object



29
30
31
32
33
34
35
# File 'lib/dsl.rb', line 29

def resolve_integer(val)
  if val.class == Integer
    val
  else
    raise "#{val} is not an integer"
  end
end

#resolve_lookup(val) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/functions/create_lookup.rb', line 44

def resolve_lookup(val)
  puts val
  puts val.class
  raise "table definition must be an array" unless val.class == Array
  val.map do |v|
    case
      when [Integer, Float].include?(v.class)
        then 3.times.map { scale_correctly(v) }
      when v.class == Array then v.map { |i| scale_correctly(i) }
    end
  end
end

#resolve_lookup_table(val) ⇒ Object



25
26
27
# File 'lib/dsl.rb', line 25

def resolve_lookup_table(val)
  val.map { |v| resolve(val) }
end

#resolve_pin_reference(val) ⇒ Object



57
58
59
60
61
62
63
# File 'lib/dsl.rb', line 57

def resolve_pin_reference(val)
  if val.match(/(^A\d+$|^\d+$)/)
    val
  else
    raise "#{val} is not a valid pin reference"
  end
end

#resolve_positive_scalar(val) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/dsl.rb', line 45

def resolve_positive_scalar(val)
  if [Integer, Float].include?(val.class)
    if val >= 0
      scale_correctly(val)
    else
      raise "#{} val should be positive"
    end
  else
    raise "#{val} must be a positive scalar number"
  end
end

#resolve_table_reference(table_ref) ⇒ Object



72
73
74
75
76
77
78
# File 'lib/functions/lookup.rb', line 72

def resolve_table_reference(table_ref)
  if $tables.has_key?(table_ref)
    table_ref
  else
    raise "table #{table_ref} invalid"
  end
end

#scale(input, lo_in, hi_in, lo_out, hi_out) ⇒ Object



62
63
64
65
66
67
68
69
70
71
# File 'lib/functions/scale.rb', line 62

def scale(input, lo_in, hi_in, lo_out, hi_out)
  h = ArbolHash.new
  h[:type] = 'scale'
  h[:input] = resolve(input)
  h[:lo_in] = resolve(lo_in)
  h[:hi_in] = resolve(hi_in)
  h[:lo_out] = resolve(lo_out)
  h[:hi_out] = resolve(hi_out)
  h
end

#scale_correctly(val) ⇒ Integer

scales float values to the integer scale representation.

Parameters:

  • val (Float|Integer)

Returns:

  • (Integer)


4
5
6
7
8
9
# File 'lib/dsl.rb', line 4

def scale_correctly(val)
  case
    when val.class == Integer then return val
    when val.class == Float then return (val * 8192).to_i
  end
end

#script_split(script) ⇒ Array<String>

separates declarations by ; delimiter

Parameters:

  • script (String)

Returns:

  • (Array<String>)


108
109
110
# File 'lib/dsl.rb', line 108

def script_split(script)
  script.split(';').select { |l| l.strip != '' }
end

#script_to_file(script, path) ⇒ Object

write the script to file



40
41
42
43
44
45
# File 'lib/arbol.rb', line 40

def script_to_file(script, path)
  puts "writing to script file #{path}"
  File.open(path, 'w') do |f|
    f.puts(script)
  end
end

#stmts_to_structure(stmts, scope) ⇒ Object

converts an array of statements to an arbol tree structure

Parameters:

  • stmts (Array<String>)
  • scope (Binding)
  • (Hash)


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/dsl.rb', line 116

def stmts_to_structure(stmts, scope)
  # defined references that can be used in the script
  refs = []
  # injection of ruby code into the eval
  # starts with the refinement, but refs will be added
  refinjects = ['using RefineBasics; ']

  # iterate all of the statements. last statement must be a strip definition
  stmts.each do |stmt|
    # ref handling
    if stmt.match(/(\w|\s)+={1}(\w|\s)+/)
      ref_name = stmt.match(/\w+/)[0]
      statements = "#{refinjects.join('')}#{stmt.split('=', 2)[1]}"
      this_ref = create_ref(
        ref_name,
        eval(statements, scope)
      )
      refinject = "#{ref_name} = ref('#{ref_name}');"
      refs << this_ref
      refinjects << refinject
    # strip handling
    elsif stmt.match('strip')
      statements = "#{refinjects.join('')} #{stmt}"
      retval = eval(statements, scope)
      retval[:refs] = refs
      return retval
    end
  end
end

#strip(lamps, pin, calc) ⇒ Hash

top level object to create a neopixel strip

Parameters:

  • lamps (Integer)
  • pin (Integer)
  • calc (ArbolHash)

Returns:

  • (Hash)


81
82
83
84
85
86
87
88
# File 'lib/dsl.rb', line 81

def strip(lamps, pin, calc)
  {
    type: 'physical_strip',
    lamps: lamps,
    pin: pin,
    calc: resolve(calc)
  }
end

#table(contents) ⇒ Object

really this is just a pass through for arrays used in lookup table creation



12
13
14
15
16
# File 'lib/functions/table.rb', line 12

def table(contents)
  if contents.class == Array
    ArbolTable.new(contents)
  end
end

#times(op1, op2) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/functions/times.rb', line 64

def times(op1, op2)
  h = ArbolHash.new
  h[:type] = 'times'
  h[:op1] = resolve(op1)
  h[:op2] = resolve(op2)
  h
end

#triangle(phase) ⇒ Object



64
65
66
67
68
69
# File 'lib/functions/triangle.rb', line 64

def triangle(phase)
  h = ArbolHash.new
  h[:type] = 'triangle'
  h[:phase] = resolve(phase)
  h
end