sparkle_motion 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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