Class: Unmagic::Color::RGB
- Inherits:
-
Unmagic::Color
- Object
- Unmagic::Color
- Unmagic::Color::RGB
- Defined in:
- lib/unmagic/color/rgb.rb,
lib/unmagic/color/rgb/hex.rb,
lib/unmagic/color/rgb/named.rb
Overview
‘RGB` (Red, Green, Blue) color representation.
## Understanding RGB
RGB is how your computer screen creates colors. Every color you see on a screen is made by combining three lights: Red, Green, and Blue. Each light can be set from ‘0` (off) to `255` (full brightness).
Think of it like mixing three flashlights:
-
‘Red=255, Green=0, Blue=0` → Pure red light
-
‘Red=0, Green=255, Blue=0` → Pure green light
-
‘Red=255, Green=255, Blue=0` → Yellow (red + green)
-
‘Red=255, Green=255, Blue=255` → White (all lights on)
-
‘Red=0, Green=0, Blue=0` → Black (all lights off)
## Why 0-255?
Computers store each color component in 8 bits (one byte), which can hold 256 different values (‘0-255`). This gives us `256³ = 16,777,216` possible colors.
## Common Formats
RGB colors can be written in different ways:
-
Hex: ‘#FF5733` (2 hex digits per component: `FF=255, 57=87, 33=51`)
-
Short hex: ‘#F73` (expanded to `#FF7733`)
-
RGB function: ‘rgb(255, 87, 51)`
-
Named colors: ‘goldenrod`, `red`, `blue` (see Named for X11 color names)
## Usage Examples
# Parse from different formats
color = Unmagic::Color::RGB.parse("#FF5733")
color = Unmagic::Color::RGB.parse("rgb(255, 87, 51)")
color = Unmagic::Color::RGB.parse("F73")
# Parse named colors (via RGB::Named or Color.parse)
color = Unmagic::Color::RGB::Named.parse("goldenrod")
color = Unmagic::Color.parse("goldenrod") # Also works
# Create directly
color = Unmagic::Color::RGB.new(red: 255, green: 87, blue: 51)
# Access components
color.red.value #=> 255
color.green.value #=> 87
color.blue.value #=> 51
# Convert to other formats
color.to_hex #=> "#ff5733"
color.to_hsl #=> HSL color
color.to_oklch #=> OKLCH color
# Generate deterministic colors from text
Unmagic::Color::RGB.derive("user@example.com".hash) #=> Consistent color for this string
Defined Under Namespace
Classes: Hex, Named, ParseError
Constant Summary
Constants inherited from Unmagic::Color
Instance Attribute Summary collapse
-
#blue ⇒ Object
readonly
Returns the value of attribute blue.
-
#green ⇒ Object
readonly
Returns the value of attribute green.
-
#red ⇒ Object
readonly
Returns the value of attribute red.
Class Method Summary collapse
-
.derive(seed, brightness: 180, saturation: 0.7) ⇒ RGB
Generate a deterministic RGB color from an integer seed.
-
.parse(input) ⇒ RGB
Parse an RGB color from a string.
-
.parse_rgb_format(input) ⇒ RGB
Parse RGB format like “rgb(255, 128, 0)” or “255, 128, 0”.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
Check if two RGB colors are equal.
-
#blend(other, amount = 0.5) ⇒ RGB
Blend this color with another color.
-
#darken(amount = 0.1) ⇒ RGB
Create a darker version by blending with black.
-
#initialize(red:, green:, blue:) ⇒ RGB
constructor
Create a new RGB color.
-
#lighten(amount = 0.1) ⇒ RGB
Create a lighter version by blending with white.
-
#luminance ⇒ Float
Calculate the relative luminance.
-
#to_hex ⇒ String
Convert to hexadecimal color string.
-
#to_hsl ⇒ HSL
Convert to HSL color space.
-
#to_oklch ⇒ OKLCH
Convert to OKLCH color space.
-
#to_rgb ⇒ RGB
Convert to RGB color space.
-
#to_s ⇒ String
Convert to string representation.
Methods inherited from Unmagic::Color
Constructor Details
Instance Attribute Details
#blue ⇒ Object (readonly)
Returns the value of attribute blue.
65 66 67 |
# File 'lib/unmagic/color/rgb.rb', line 65 def blue @blue end |
#green ⇒ Object (readonly)
Returns the value of attribute green.
65 66 67 |
# File 'lib/unmagic/color/rgb.rb', line 65 def green @green end |
#red ⇒ Object (readonly)
Returns the value of attribute red.
65 66 67 |
# File 'lib/unmagic/color/rgb.rb', line 65 def red @red end |
Class Method Details
.derive(seed, brightness: 180, saturation: 0.7) ⇒ RGB
Generate a deterministic RGB color from an integer seed.
This creates consistent, visually distinct colors from hash values or IDs. The same seed always produces the same color, making it useful for:
-
User avatars (hash their email/username)
-
Syntax highlighting (hash the token type)
-
Data visualization (hash category names)
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 |
# File 'lib/unmagic/color/rgb.rb', line 142 def derive(seed, brightness: 180, saturation: 0.7) raise ArgumentError, "Seed must be an integer" unless seed.is_a?(Integer) h32 = seed & 0xFFFFFFFF # Ensure 32-bit # Extract RGB components from different parts of the hash r_base = (h32 & 0xFF) g_base = ((h32 >> 8) & 0xFF) b_base = ((h32 >> 16) & 0xFF) # Apply brightness and saturation adjustments # Brightness controls the average RGB value # Saturation controls how much the channels differ from each other avg = (r_base + g_base + b_base) / 3.0 # Adjust each channel relative to average r = avg + (r_base - avg) * saturation g = avg + (g_base - avg) * saturation b = avg + (b_base - avg) * saturation # Scale to target brightness scale = brightness / 127.5 # 127.5 is middle of 0-255 r = (r * scale).clamp(0, 255).round g = (g * scale).clamp(0, 255).round b = (b * scale).clamp(0, 255).round new(red: r, green: g, blue: b) end |
.parse(input) ⇒ RGB
Parse an RGB color from a string.
Accepts multiple formats:
-
Hex with hash: “#FF8800”, “#F80”
-
Hex without hash: “FF8800”, “F80”
-
RGB function: “rgb(255, 128, 0)”
-
Raw values: “255, 128, 0”
105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/unmagic/color/rgb.rb', line 105 def parse(input) raise ParseError, "Input must be a string" unless input.is_a?(::String) input = input.strip # Check if it looks like a hex color (starts with # or only contains hex digits) if input.start_with?("#") || input.match?(/\A[0-9A-Fa-f]{3,6}\z/) return Hex.parse(input) end # Try to parse as RGB format parse_rgb_format(input) end |
.parse_rgb_format(input) ⇒ RGB
Parse RGB format like “rgb(255, 128, 0)” or “255, 128, 0”
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/unmagic/color/rgb.rb', line 177 def parse_rgb_format(input) # Remove rgb() wrapper if present clean = input.gsub(/^rgb\s*\(\s*|\s*\)$/, "").strip # Split values values = clean.split(/\s*,\s*/) unless values.length == 3 raise ParseError, "Expected 3 RGB values, got #{values.length}" end # Check if all values are numeric (allow negative for clamping) values.each_with_index do |v, i| unless v.match?(/\A-?\d+\z/) component = ["red", "green", "blue"][i] raise ParseError, "Invalid #{component} value: #{v.inspect} (must be a number)" end end # Convert to integers (constructor will clamp) parsed = values.map(&:to_i) new(red: parsed[0], green: parsed[1], blue: parsed[2]) end |
Instance Method Details
#==(other) ⇒ Boolean
Check if two RGB colors are equal.
373 374 375 376 377 378 |
# File 'lib/unmagic/color/rgb.rb', line 373 def ==(other) other.is_a?(Unmagic::Color::RGB) && @red == other.red && @green == other.green && @blue == other.blue end |
#blend(other, amount = 0.5) ⇒ RGB
Blend this color with another color.
Blends in RGB space by linearly interpolating each component.
334 335 336 337 338 339 340 341 342 343 |
# File 'lib/unmagic/color/rgb.rb', line 334 def blend(other, amount = 0.5) amount = amount.to_f.clamp(0, 1) other_rgb = other.respond_to?(:to_rgb) ? other.to_rgb : other Unmagic::Color::RGB.new( red: (@red.value * (1 - amount) + other_rgb.red.value * amount).round, green: (@green.value * (1 - amount) + other_rgb.green.value * amount).round, blue: (@blue.value * (1 - amount) + other_rgb.blue.value * amount).round, ) end |
#darken(amount = 0.1) ⇒ RGB
Create a darker version by blending with black.
365 366 367 |
# File 'lib/unmagic/color/rgb.rb', line 365 def darken(amount = 0.1) blend(Unmagic::Color::RGB.new(red: 0, green: 0, blue: 0), amount) end |
#lighten(amount = 0.1) ⇒ RGB
Create a lighter version by blending with white.
353 354 355 |
# File 'lib/unmagic/color/rgb.rb', line 353 def lighten(amount = 0.1) blend(Unmagic::Color::RGB.new(red: 255, green: 255, blue: 255), amount) end |
#luminance ⇒ Float
Calculate the relative luminance.
This is the perceived brightness of the color according to the WCAG specification, accounting for how the human eye responds differently to red, green, and blue light.
305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/unmagic/color/rgb.rb', line 305 def luminance r = @red.value / 255.0 g = @green.value / 255.0 b = @blue.value / 255.0 r = r <= 0.03928 ? r / 12.92 : ((r + 0.055) / 1.055)**2.4 g = g <= 0.03928 ? g / 12.92 : ((g + 0.055) / 1.055)**2.4 b = b <= 0.03928 ? b / 12.92 : ((b + 0.055) / 1.055)**2.4 0.2126 * r + 0.7152 * g + 0.0722 * b end |
#to_hex ⇒ String
Convert to hexadecimal color string.
Returns a lowercase hex string with hash prefix, always 6 characters (2 per component).
222 223 224 |
# File 'lib/unmagic/color/rgb.rb', line 222 def to_hex format("#%02x%02x%02x", @red.value, @green.value, @blue.value) end |
#to_hsl ⇒ HSL
Convert to HSL color space.
Converts this RGB color to HSL (Hue, Saturation, Lightness). HSL is often more intuitive for color manipulation.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/unmagic/color/rgb.rb', line 240 def to_hsl r = @red.value / 255.0 g = @green.value / 255.0 b = @blue.value / 255.0 max = [r, g, b].max min = [r, g, b].min delta = max - min # Lightness l = (max + min) / 2.0 if delta == 0 # Achromatic h = 0 s = 0 else # Saturation s = l > 0.5 ? delta / (2.0 - max - min) : delta / (max + min) # Hue h = case max when r then ((g - b) / delta + (g < b ? 6 : 0)) / 6.0 when g then ((b - r) / delta + 2) / 6.0 when b then ((r - g) / delta + 4) / 6.0 end end Unmagic::Color::HSL.new(hue: (h * 360).round, saturation: (s * 100).round, lightness: (l * 100).round) end |
#to_oklch ⇒ OKLCH
This is currently a simplified approximation.
Convert to OKLCH color space.
Converts this RGB color to OKLCH (Lightness, Chroma, Hue).
277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/unmagic/color/rgb.rb', line 277 def to_oklch # For now, simple approximation based on RGB -> HSL -> OKLCH # This is a simplified placeholder require_relative "oklch" # Convert lightness roughly from RGB luminance l = luminance # Approximate chroma from saturation and lightness hsl = to_hsl c = (hsl.saturation / 100.0) * 0.2 * (1 - (l - 0.5).abs * 2) h = hsl.hue Unmagic::Color::OKLCH.new(lightness: l, chroma: c, hue: h) end |
#to_rgb ⇒ RGB
Convert to RGB color space.
Since this is already an RGB color, returns self.
207 208 209 |
# File 'lib/unmagic/color/rgb.rb', line 207 def to_rgb self end |
#to_s ⇒ String
Convert to string representation.
Returns the hex representation of the color.
385 386 387 |
# File 'lib/unmagic/color/rgb.rb', line 385 def to_s to_hex end |