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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +22 -0
- data/README.md +47 -0
- data/bin/sm-discover +18 -0
- data/bin/sm-mark-lights +58 -0
- data/bin/sm-off +38 -0
- data/bin/sm-on +50 -0
- data/bin/sm-simulate +595 -0
- data/bin/sm-watch-memory +9 -0
- data/bin/sparkle-motion +48 -0
- data/config.yml +201 -0
- data/data/dynamic/bridges.csv +5 -0
- data/data/static/devices.csv +7 -0
- data/lib/sparkle_motion.rb +72 -0
- data/lib/sparkle_motion/config.rb +45 -0
- data/lib/sparkle_motion/env.rb +14 -0
- data/lib/sparkle_motion/http.rb +26 -0
- data/lib/sparkle_motion/hue/ssdp.rb +39 -0
- data/lib/sparkle_motion/launch_pad/color.rb +41 -0
- data/lib/sparkle_motion/launch_pad/widget.rb +187 -0
- data/lib/sparkle_motion/launch_pad/widgets/button.rb +29 -0
- data/lib/sparkle_motion/launch_pad/widgets/horizontal_slider.rb +43 -0
- data/lib/sparkle_motion/launch_pad/widgets/radio_group.rb +53 -0
- data/lib/sparkle_motion/launch_pad/widgets/toggle.rb +48 -0
- data/lib/sparkle_motion/launch_pad/widgets/vertical_slider.rb +43 -0
- data/lib/sparkle_motion/lazy_request_config.rb +87 -0
- data/lib/sparkle_motion/node.rb +80 -0
- data/lib/sparkle_motion/nodes/generator.rb +8 -0
- data/lib/sparkle_motion/nodes/generators/const.rb +15 -0
- data/lib/sparkle_motion/nodes/generators/perlin.rb +26 -0
- data/lib/sparkle_motion/nodes/generators/wave2.rb +20 -0
- data/lib/sparkle_motion/nodes/transform.rb +34 -0
- data/lib/sparkle_motion/nodes/transforms/contrast.rb +20 -0
- data/lib/sparkle_motion/nodes/transforms/range.rb +41 -0
- data/lib/sparkle_motion/nodes/transforms/spotlight.rb +35 -0
- data/lib/sparkle_motion/output.rb +69 -0
- data/lib/sparkle_motion/results.rb +78 -0
- data/lib/sparkle_motion/utility.rb +68 -0
- data/lib/sparkle_motion/vector2.rb +11 -0
- data/lib/sparkle_motion/version.rb +3 -0
- data/sparkle_motion.gemspec +44 -0
- 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
|