effects.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package rgb
  2. import (
  3. "fmt"
  4. "math"
  5. "strconv"
  6. )
  7. // Effect defines an RGB lighting effect.
  8. type Effect uint8
  9. const (
  10. Static Effect = 0
  11. Breathing Effect = 1
  12. EffectAlmond Effect = 2
  13. BandSat Effect = 3
  14. BandVal Effect = 4
  15. BandHue Effect = 5
  16. Chroma Effect = 6
  17. Rainbow Effect = 7
  18. RainbowWave Effect = 8
  19. Dance Effect = 9
  20. RainbowDance Effect = 10
  21. ChromaDance Effect = 11
  22. Snake Effect = 12
  23. Tail Effect = 13
  24. Split Effect = 14
  25. Heartbeat Effect = 15
  26. Blink Effect = 16
  27. BlinkStatic Effect = 17
  28. Gradient Effect = 18
  29. Testing Effect = 19
  30. )
  31. // EffectName returns the human-readable name of an effect.
  32. func (e Effect) String() string {
  33. names := map[Effect]string{
  34. Static: "static",
  35. Breathing: "breathing",
  36. EffectAlmond: "almond",
  37. BandSat: "band_sat",
  38. BandVal: "band_val",
  39. BandHue: "band_hue",
  40. Chroma: "chroma",
  41. Rainbow: "rainbow",
  42. RainbowWave: "rainbow_wave",
  43. Dance: "dance",
  44. RainbowDance: "rainbow_dance",
  45. ChromaDance: "chroma_dance",
  46. Snake: "snake",
  47. Tail: "tail",
  48. Split: "split",
  49. Heartbeat: "heartbeat",
  50. Blink: "blink",
  51. BlinkStatic: "blink_static",
  52. Gradient: "gradient",
  53. Testing: "testing",
  54. }
  55. if name, ok := names[e]; ok {
  56. return name
  57. }
  58. return "unknown"
  59. }
  60. // ParseEffect converts a string name to an Effect.
  61. func ParseEffect(name string) (Effect, error) {
  62. switch name {
  63. case "static":
  64. return Static, nil
  65. case "breathing":
  66. return Breathing, nil
  67. case "almond":
  68. return EffectAlmond, nil
  69. case "band_sat":
  70. return BandSat, nil
  71. case "band_val":
  72. return BandVal, nil
  73. case "band_hue":
  74. return BandHue, nil
  75. case "chroma":
  76. return Chroma, nil
  77. case "rainbow":
  78. return Rainbow, nil
  79. case "rainbow_wave":
  80. return RainbowWave, nil
  81. case "dance":
  82. return Dance, nil
  83. case "rainbow_dance":
  84. return RainbowDance, nil
  85. case "chroma_dance":
  86. return ChromaDance, nil
  87. case "snake":
  88. return Snake, nil
  89. case "tail":
  90. return Tail, nil
  91. case "split":
  92. return Split, nil
  93. case "heartbeat":
  94. return Heartbeat, nil
  95. case "blink":
  96. return Blink, nil
  97. case "blink_static":
  98. return BlinkStatic, nil
  99. case "gradient":
  100. return Gradient, nil
  101. case "testing":
  102. return Testing, nil
  103. default:
  104. return 0, fmt.Errorf("unknown effect: %s", name)
  105. }
  106. }
  107. // LEDParam defines QMK rgblight parameter indices.
  108. type LEDParam uint8
  109. const (
  110. Brightness LEDParam = 1
  111. EffectID LEDParam = 2
  112. Speed LEDParam = 3
  113. ColorValue LEDParam = 4
  114. Mode LEDParam = EffectID
  115. Enable LEDParam = EffectID
  116. )
  117. // Color represents an RGB color.
  118. type Color struct {
  119. R uint8
  120. G uint8
  121. B uint8
  122. }
  123. // ParseHexColor parses a 6-digit hex color string (e.g. "ff0000").
  124. func ParseHexColor(s string) (Color, error) {
  125. if len(s) != 6 {
  126. return Color{}, fmt.Errorf("invalid hex color: %s", s)
  127. }
  128. r, err := strconv.ParseUint(s[0:2], 16, 8)
  129. if err != nil {
  130. return Color{}, fmt.Errorf("invalid hex color: %s", s)
  131. }
  132. g, err := strconv.ParseUint(s[2:4], 16, 8)
  133. if err != nil {
  134. return Color{}, fmt.Errorf("invalid hex color: %s", s)
  135. }
  136. b, err := strconv.ParseUint(s[4:6], 16, 8)
  137. if err != nil {
  138. return Color{}, fmt.Errorf("invalid hex color: %s", s)
  139. }
  140. return Color{R: uint8(r), G: uint8(g), B: uint8(b)}, nil
  141. }
  142. // State represents the current RGB state from the keyboard.
  143. type State struct {
  144. Enabled bool
  145. Mode Effect
  146. Brightness uint8
  147. Speed uint8
  148. Color Color
  149. }
  150. // HSV converts the color to HSV (QMK rgblight uses 0-255 ranges for H, S, V).
  151. func (c Color) HSV() (h, s, v uint8) {
  152. r, g, b := float64(c.R), float64(c.G), float64(c.B)
  153. max := math.Max(r, math.Max(g, b))
  154. min := math.Min(r, math.Min(g, b))
  155. v = uint8(max)
  156. d := max - min
  157. if d == 0 {
  158. return 0, 0, v
  159. }
  160. if max > 0 {
  161. s = uint8((d / max) * 255)
  162. } else {
  163. return 0, 0, 0
  164. }
  165. var hFloat float64
  166. switch {
  167. case max == r:
  168. hFloat = ((g - b) / d) * 60.0
  169. case max == g:
  170. hFloat = ((b-r)/d)*60.0 + 120.0
  171. case max == b:
  172. hFloat = ((r-g)/d)*60.0 + 240.0
  173. }
  174. if hFloat < 0 {
  175. hFloat += 360.0
  176. }
  177. h = uint8((hFloat / 360.0) * 255.0)
  178. if h >= 255 {
  179. h = 254
  180. }
  181. return h, s, v
  182. }