sparkle_motion 0.1.0

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 (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/LICENSE +22 -0
  4. data/README.md +47 -0
  5. data/bin/sm-discover +18 -0
  6. data/bin/sm-mark-lights +58 -0
  7. data/bin/sm-off +38 -0
  8. data/bin/sm-on +50 -0
  9. data/bin/sm-simulate +595 -0
  10. data/bin/sm-watch-memory +9 -0
  11. data/bin/sparkle-motion +48 -0
  12. data/config.yml +201 -0
  13. data/data/dynamic/bridges.csv +5 -0
  14. data/data/static/devices.csv +7 -0
  15. data/lib/sparkle_motion.rb +72 -0
  16. data/lib/sparkle_motion/config.rb +45 -0
  17. data/lib/sparkle_motion/env.rb +14 -0
  18. data/lib/sparkle_motion/http.rb +26 -0
  19. data/lib/sparkle_motion/hue/ssdp.rb +39 -0
  20. data/lib/sparkle_motion/launch_pad/color.rb +41 -0
  21. data/lib/sparkle_motion/launch_pad/widget.rb +187 -0
  22. data/lib/sparkle_motion/launch_pad/widgets/button.rb +29 -0
  23. data/lib/sparkle_motion/launch_pad/widgets/horizontal_slider.rb +43 -0
  24. data/lib/sparkle_motion/launch_pad/widgets/radio_group.rb +53 -0
  25. data/lib/sparkle_motion/launch_pad/widgets/toggle.rb +48 -0
  26. data/lib/sparkle_motion/launch_pad/widgets/vertical_slider.rb +43 -0
  27. data/lib/sparkle_motion/lazy_request_config.rb +87 -0
  28. data/lib/sparkle_motion/node.rb +80 -0
  29. data/lib/sparkle_motion/nodes/generator.rb +8 -0
  30. data/lib/sparkle_motion/nodes/generators/const.rb +15 -0
  31. data/lib/sparkle_motion/nodes/generators/perlin.rb +26 -0
  32. data/lib/sparkle_motion/nodes/generators/wave2.rb +20 -0
  33. data/lib/sparkle_motion/nodes/transform.rb +34 -0
  34. data/lib/sparkle_motion/nodes/transforms/contrast.rb +20 -0
  35. data/lib/sparkle_motion/nodes/transforms/range.rb +41 -0
  36. data/lib/sparkle_motion/nodes/transforms/spotlight.rb +35 -0
  37. data/lib/sparkle_motion/output.rb +69 -0
  38. data/lib/sparkle_motion/results.rb +78 -0
  39. data/lib/sparkle_motion/utility.rb +68 -0
  40. data/lib/sparkle_motion/vector2.rb +11 -0
  41. data/lib/sparkle_motion/version.rb +3 -0
  42. data/sparkle_motion.gemspec +44 -0
  43. metadata +178 -0
@@ -0,0 +1,41 @@
1
+ module SparkleMotion
2
+ module LaunchPad
3
+ # Color classes for the Novation Launchpad Mk 2, which supports 4 bits per
4
+ # color element in RGB mode.
5
+ class Color
6
+ attr_reader :r, :g, :b
7
+ def initialize(r, g, b)
8
+ @r = clamp_elem(r)
9
+ @g = clamp_elem(g)
10
+ @b = clamp_elem(b)
11
+ end
12
+
13
+ def to_h
14
+ { r: r,
15
+ g: g,
16
+ b: b }
17
+ end
18
+
19
+ protected
20
+
21
+ def clamp_elem(elem)
22
+ return 0x00 if elem < 0x00
23
+ return 0x3F if elem > 0x3F # 63
24
+ elem
25
+ end
26
+ end
27
+
28
+ #
29
+ class Color
30
+ BLACK = new(0x00, 0x00, 0x00).freeze
31
+ DARK_GRAY = new(0x07, 0x07, 0x07).freeze
32
+ LIGHT_GRAY = new(0x27, 0x27, 0x27).freeze
33
+ WHITE = new(0x3F, 0x3F, 0x3F).freeze
34
+ RED = new(0x3F, 0x00, 0x00).freeze
35
+ DARK_GREEN = new(0x00, 0x07, 0x00).freeze
36
+ GREEN = new(0x00, 0x3F, 0x00).freeze
37
+ LIGHT_GREEN = new(0x10, 0x4F, 0x10).freeze
38
+ BLUE = new(0x00, 0x00, 0x3F).freeze
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,187 @@
1
+ module SparkleMotion
2
+ module LaunchPad
3
+ # Base class for Launchpad UI widgets.
4
+ class Widget
5
+ attr_reader :value, :x, :y, :width, :height
6
+ attr_accessor :on, :off, :down
7
+
8
+ # TODO: Use `Vector2` for position/size...
9
+ def initialize(launchpad:, x: nil, y: nil, position: nil, width:, height:, on:, off:, down:,
10
+ value:)
11
+ @x = x
12
+ @y = y
13
+ @position = position
14
+ @width = width
15
+ @height = height
16
+ @launchpad = launchpad
17
+ @value = value
18
+ @pressed = {}
19
+ set_colors!(on, off, down)
20
+ attach_handler!
21
+ end
22
+
23
+ def update(value, render_now = true)
24
+ @value = value
25
+ @value = max_v if max_v && @value && @value > max_v
26
+ render if render_now
27
+ end
28
+
29
+ def render
30
+ @pressed.map do |idx, state|
31
+ next unless state
32
+ if @x
33
+ xx, yy = coords_for(idx: idx)
34
+ change_grid(x: xx, y: yy, color: down)
35
+ else
36
+ change_command(position: @position, color: down)
37
+ end
38
+ end
39
+ end
40
+
41
+ def blank
42
+ black = SparkleMotion::LaunchPad::Color::BLACK.to_h
43
+ if @x
44
+ (0..max_x).each do |xx|
45
+ (0..max_y).each do |yy|
46
+ change_grid(x: xx, y: yy, color: black)
47
+ end
48
+ end
49
+ else
50
+ change_command(position: @position, color: black)
51
+ end
52
+ end
53
+
54
+ def max_v
55
+ if @x
56
+ @max_v ||= (height * width) - 1
57
+ else
58
+ 1
59
+ end
60
+ end
61
+
62
+ protected
63
+
64
+ attr_reader :launchpad
65
+
66
+ def tag
67
+ @tag ||= begin
68
+ pos = @x ? "#{@x},#{@y}" : @position
69
+ "#{self.class.name}(#{pos})"
70
+ end
71
+ end
72
+
73
+ def pressed!(x: nil, y: nil, position: nil)
74
+ idx = x ? index_for(x: x, y: y) : position
75
+ @pressed[idx] = true
76
+ end
77
+
78
+ def released!(x: nil, y: nil, position: nil)
79
+ idx = x ? index_for(x: x, y: y) : position
80
+ @pressed.delete(idx) if @pressed.key?(idx)
81
+ end
82
+
83
+ def handle_grid_response(action)
84
+ guard_call(tag) do
85
+ xx = action[:x] - @x
86
+ yy = action[:y] - @y
87
+ if action[:state] == :down
88
+ pressed!(x: xx, y: yy)
89
+ on_down(x: xx, y: yy)
90
+ else
91
+ released!(x: xx, y: yy)
92
+ on_up(x: xx, y: yy)
93
+ end
94
+ end
95
+ end
96
+
97
+ def handle_command_response(action)
98
+ guard_call(tag) do
99
+ if action[:state] == :down
100
+ pressed!(position: @position)
101
+ on_down(position: @position)
102
+ else
103
+ released!(position: @position)
104
+ on_up(position: @position)
105
+ end
106
+ end
107
+ end
108
+
109
+ def index_for(x:, y:); (y * width) + x; end
110
+ def coords_for(idx:); [idx / width, idx % width]; end
111
+
112
+ # Defaults that you may want to override:
113
+ def on_down(x: nil, y: nil, position: nil)
114
+ if @x
115
+ change_grid(x: x, y: y, color: down)
116
+ else
117
+ change_command(position: position, color: down)
118
+ end
119
+ end
120
+
121
+ # rubocop:disable Lint/UnusedMethodArgument
122
+ def on_up(x: nil, y: nil, position: nil); render; end
123
+ # rubocop:enable Lint/UnusedMethodArgument
124
+
125
+ # Internal utilities for you to use:
126
+ def change_grid(x:, y:, color:)
127
+ return if (x > max_x) || (x < 0)
128
+ return if (y > max_y) || (y < 0)
129
+ col = effective_color(x: x, y: y, color: color)
130
+ grid_apply_color(x, y, col)
131
+ end
132
+
133
+ def effective_color(x:, y:, color:)
134
+ color.is_a?(Array) ? color[index_for(x: x, y: y)] : color
135
+ end
136
+
137
+ def change_command(position:, color:)
138
+ return unless launchpad
139
+ launchpad.device.change_command(position, color[:r], color[:g], color[:b])
140
+ end
141
+
142
+ def grid_apply_color(x, y, color)
143
+ return unless launchpad
144
+ launchpad.device.change_grid(x + @x, y + @y, color[:r], color[:g], color[:b])
145
+ end
146
+
147
+ def max_y; @max_y ||= height - 1; end
148
+ def max_x; @max_x ||= width - 1; end
149
+
150
+ private
151
+
152
+ def set_colors!(on, off, down)
153
+ @on = normalize_color!(on)
154
+ @off = normalize_color!(off)
155
+ @down = normalize_color!(down)
156
+ end
157
+
158
+ def normalize_color!(color)
159
+ black = SparkleMotion::LaunchPad::Color::BLACK.to_h
160
+ color.is_a?(Array) ? color.map { |oo| black.merge(oo) } : black.merge(color)
161
+ end
162
+
163
+ def attach_handler!
164
+ attach_grid_handler!
165
+ attach_position_handler!
166
+ end
167
+
168
+ def attach_grid_handler!
169
+ return unless @x
170
+ return unless launchpad
171
+ xx = @x..(@x + max_x)
172
+ yy = @y..(@y + max_y)
173
+ launchpad.response_to(:grid, :both, x: xx, y: yy) do |_inter, action|
174
+ handle_grid_response(action)
175
+ end
176
+ end
177
+
178
+ def attach_position_handler!
179
+ return if @x
180
+ return unless launchpad
181
+ launchpad.response_to(@position, :both) do |_inter, action|
182
+ handle_command_response(action)
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "./toggle"
2
+
3
+ module SparkleMotion
4
+ module LaunchPad
5
+ module Widgets
6
+ # Class to represent a single stateless button.
7
+ class Button < Toggle
8
+ def initialize(launchpad:,
9
+ position: nil,
10
+ x: nil,
11
+ y: nil,
12
+ color:,
13
+ down:,
14
+ on_press: nil,
15
+ value: 0)
16
+ super(launchpad: launchpad,
17
+ position: position,
18
+ x: x,
19
+ y: y,
20
+ on: color,
21
+ off: color,
22
+ down: down,
23
+ value: value,
24
+ on_press: on_press)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ module SparkleMotion
2
+ module LaunchPad
3
+ module Widgets
4
+ # Class to represent a slider-style control on a Novation Launchpad.
5
+ class HorizontalSlider < Widget
6
+ attr_accessor :on_change
7
+
8
+ def initialize(launchpad:, x:, y:, size:, on:, off:, down:, on_change: nil, value: 0)
9
+ super(launchpad: launchpad,
10
+ x: x,
11
+ y: y,
12
+ width: size,
13
+ height: 1,
14
+ on: on,
15
+ off: off,
16
+ down: down,
17
+ value: value)
18
+ @on_change = on_change
19
+ end
20
+
21
+ def render
22
+ (0..max_v).each do |xx|
23
+ change_grid(x: xx, y: 0, color: (value && value >= xx) ? on : off)
24
+ end
25
+ super
26
+ end
27
+
28
+ def update(*args)
29
+ super(*args)
30
+ on_change.call(value) if on_change
31
+ end
32
+
33
+ protected
34
+
35
+ def on_down(x:, y:)
36
+ @value = x
37
+ super(x: x, y: y)
38
+ on_change.call(value) if on_change
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,53 @@
1
+ module SparkleMotion
2
+ module LaunchPad
3
+ module Widgets
4
+ # Class to represent a radio-button group control on a Novation Launchpad.
5
+ class RadioGroup < Widget
6
+ attr_accessor :on_select, :on_deselect
7
+
8
+ def initialize(launchpad:, x:, y:, size:, on:, off:, down:, on_select: nil, on_deselect:,
9
+ value: nil)
10
+ super(launchpad: launchpad,
11
+ x: x,
12
+ y: y,
13
+ width: size[0],
14
+ height: size[1],
15
+ on: on,
16
+ off: off,
17
+ down: down,
18
+ value: value)
19
+ @on_select = on_select
20
+ @on_deselect = on_deselect
21
+ end
22
+
23
+ def render
24
+ (0..max_x).each do |xx|
25
+ (0..max_y).each do |yy|
26
+ col = (value == index_for(x: xx, y: yy)) ? on : off
27
+
28
+ change_grid(x: xx, y: yy, color: col)
29
+ end
30
+ end
31
+ end
32
+
33
+ def update(*args)
34
+ super(*args)
35
+ on_select.call(value) if on_select && value
36
+ on_deselect.call(value) if on_deselect && !value
37
+ end
38
+
39
+ protected
40
+
41
+ def on_down(x:, y:)
42
+ vv = index_for(x: x, y: y)
43
+ vv = nil if value == vv
44
+ @value = vv
45
+ super(x: x, y: y)
46
+
47
+ handler = value ? on_select : on_deselect
48
+ handler.call(value) if handler
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,48 @@
1
+ module SparkleMotion
2
+ module LaunchPad
3
+ module Widgets
4
+ # Class to represent a single toggle button.
5
+ class Toggle < Widget
6
+ attr_accessor :on_press
7
+
8
+ def initialize(launchpad:, position: nil, x: nil, y: nil, on:, off:, down:,
9
+ on_press: nil, value: 0)
10
+ super(launchpad: launchpad,
11
+ position: position,
12
+ x: x,
13
+ y: y,
14
+ width: 1,
15
+ height: 1,
16
+ on: on,
17
+ off: off,
18
+ down: down,
19
+ value: value)
20
+ @on_press = on_press
21
+ end
22
+
23
+ def render
24
+ val = (value != 0) ? on : off
25
+ if @x
26
+ change_grid(x: 0, y: 0, color: val)
27
+ else
28
+ change_command(position: @position, color: val)
29
+ end
30
+ super
31
+ end
32
+
33
+ def update(new_val)
34
+ @value = new_val ? 1 : 0
35
+ render
36
+ end
37
+
38
+ protected
39
+
40
+ def on_down(x: nil, y: :nil, position: nil)
41
+ @value = (@value == 0) ? 1 : 0
42
+ super(x: x, y: y, position: position)
43
+ on_press.call(value) if on_press
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ module SparkleMotion
2
+ module LaunchPad
3
+ module Widgets
4
+ # Class to represent a slider-style control on a Novation Launchpad.
5
+ class VerticalSlider < Widget
6
+ attr_accessor :on_change
7
+
8
+ def initialize(launchpad:, x:, y:, size:, on:, off:, down:, on_change: nil, value: 0)
9
+ super(launchpad: launchpad,
10
+ x: x,
11
+ y: y,
12
+ width: 1,
13
+ height: size,
14
+ on: on,
15
+ off: off,
16
+ down: down,
17
+ value: value)
18
+ @on_change = on_change
19
+ end
20
+
21
+ def render
22
+ (0..max_v).each do |yy|
23
+ change_grid(x: 0, y: yy, color: (value && value >= yy) ? on : off)
24
+ end
25
+ super
26
+ end
27
+
28
+ def update(*args)
29
+ super(*args)
30
+ on_change.call(value) if on_change
31
+ end
32
+
33
+ protected
34
+
35
+ def on_down(x:, y:)
36
+ @value = y
37
+ super(x: x, y: y)
38
+ on_change.call(value) if on_change
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end