forked from polychromatic/polychromatic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfx.py
210 lines (169 loc) · 6.64 KB
/
fx.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
103
104
105
106
107
108
109
110
111
112
113
114
115
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# Polychromatic is licensed under the GPLv3.
# Copyright (C) 2020-2024 Luke Horwell <[email protected]>
"""
This module is a higher level interface for custom effects for devices that
support it, regardless of vendor.
This is used internally for rendering software effects as well as "scripted"
effects written by users.
To achieve effects, colours may be converted between different colour spaces:
- RGB (Red, Green, Blue) -> using hex codes for storage
- HSV (Hue, Saturation, Value) -> for colour intensity
- HSL (Hue, Saturation, Lightness) -> for black/white intensity
"""
import colour
import math
from . import common
class FX(object):
"""
Backends use this class for the get_device_object() functionality. This
is for supporting individual LED lighting where supported by the device.
This object is the 'fx' object used in custom effect scripts as well as
Polychromatic internally for software effects.
All classes stubbed as NotImplementedError should be implemented.
"""
def __init__(self):
self.name = "Unknown Device"
self.form_factor_id = "unrecognised"
self.rows = 0
self.cols = 0
#######################################################
# To be implemented by the backend
# See also: _backend.DeviceItem.Matrix()
#######################################################
def init(self):
"""
Prepare the device for custom frames. If unnecessary, this can be ignored.
"""
return
def set(self, x=0, y=0, red=255, green=255, blue=255):
"""
Set a colour at the specified co-ordinate.
"""
raise NotImplementedError
def draw(self):
"""
Send the data to the hardware to be displayed.
"""
raise NotImplementedError
def clear(self):
"""
Reset all LEDs to an off state.
"""
raise NotImplementedError
def brightness(self, percent):
"""
Set the global brightness of the LEDs. Could be used for fade effects globally.
"""
raise NotImplementedError
#######################################################
# Functions for scripting
#######################################################
def rgb_to_hex(self, red, green, blue):
"""
Converts the specified RGB values into a HEX string.
Input: (0, 255, 0)
Output: "#00FF00"
"""
return common.rgb_to_hex([red, green, blue])
def hex_to_rgb(self, value):
"""
Converts a HEX colour string to a RGB list.
Input: "#FF0000"
Output: [255, 0, 0]
"""
return common.hex_to_rgb(value)
def saturate_hex(self, hex_value, amount):
"""
Change the saturation of a colour by a relative amount (-1 to 1) and
return the new colour hex value.
Params:
hex_value (str) Hex value
amount (float) Relative amount to saturate (-1 to 1)
"""
c = colour.Color(hex_value)
# Value must be between 0 and 1
new_value = c.get_saturation() + amount
if new_value < 0:
new_value = 0
elif new_value > 1:
new_value = 1
c.set_saturation(new_value)
return c.get_hex_l()
def saturate_rgb(self, rgb, amount):
"""
Alias to saturate_hex(), but inputs/outputs RGB values.
Params:
rgb (list) List of RGB integers [red, green, blue]
amount (float) Relative amount to saturate (-1 to 1)
"""
return self.hex_to_rgb(self.saturate_hex(self.rgb_to_hex(rgb[0], rgb[1], rgb[2]), amount))
def hue_hex(self, hex_value, amount):
"""
Change the hue of a colour by a relative amount (-1 to 1) and
return the new colour hex value.
Params:
hex_value (str) Hex value
amount (float) Relative amount to cycle hue (-1 to 1)
"""
c = colour.Color(hex_value)
c.set_hue(c.get_hue() + amount)
return c.get_hex_l()
def hue_rgb(self, rgb, amount):
"""
Alias to hue_hex(), but inputs/outputs RGB values.
Params:
rgb (list) List of RGB integers [red, green, blue]
amount (float) Relative amount to cycle hue (-1 to 1)
"""
return self.hex_to_rgb(self.hue_hex(self.rgb_to_hex(rgb[0], rgb[1], rgb[2]), amount))
def lightness_hex(self, hex_value, amount):
"""
Change the lightness of a colour by a relative amount (-1 to 1) and
return the new colour hex value.
Params:
hex_value (str) Hex value
amount (float) Relative amount to cycle hue (-1 to 1)
"""
c = colour.Color(hex_value)
# Value must be between 0 and 1
new_value = c.get_luminance() + amount
if new_value < 0:
new_value = 0
elif new_value > 1:
new_value = 1
c.set_luminance(new_value)
return c.get_hex_l()
def lightness_rgb(self, rgb, amount):
"""
Alias to lightness_hex(), but inputs/outputs RGB values.
Params:
rgb (list) List of RGB integers [red, green, blue]
amount (float) Relative amount to cycle hue (-1 to 1)
"""
return self.hex_to_rgb(self.lightness_hex(self.rgb_to_hex(rgb[0], rgb[1], rgb[2]), amount))
def gradient(self, colours=[], steps=0):
"""
Return a list of colours that builds a gradient from start to finish
using the specified colours (equal distance between each step)
For best results, make sure the steps are divisable by the number
of colours. Otherwise, you may will get more colours then expected.
For example, ["#000000", "#FFFFFF"] (black, white) across 10 steps
makes the 5th colour the midpoint (grey)
Params:
colours (list) List of colours for the gradient
steps (int) Total colours to return for rendering the gradient
"""
if len(colours) < 2:
raise ValueError("Insufficient colours! At least 2 required to generate gradient.")
output = []
steps_between_stops = math.ceil(steps / (len(colours) - 1))
for index, item in enumerate(colours):
try:
next_colour = colours[index + 1]
except IndexError:
# This is the last colour
continue
c = colour.Color(item)
for c2 in list(c.range_to(next_colour, steps_between_stops)):
output.append(c2.get_hex_l())
return output