Class: Rich::ColorTriplet

Inherits:
Object
  • Object
show all
Defined in:
lib/rich/color_triplet.rb

Overview

Represents an RGB color triplet with values from 0-255. This is an immutable value object used for true color representation.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(red, green, blue) ⇒ ColorTriplet

Create a new color triplet

Parameters:

  • red (Integer)

    Red component (0-255)

  • green (Integer)

    Green component (0-255)

  • blue (Integer)

    Blue component (0-255)



20
21
22
23
24
25
# File 'lib/rich/color_triplet.rb', line 20

def initialize(red, green, blue)
  @red = clamp_component(red)
  @green = clamp_component(green)
  @blue = clamp_component(blue)
  freeze
end

Instance Attribute Details

#blueInteger (readonly)

Returns Blue component (0-255).

Returns:

  • (Integer)

    Blue component (0-255)



14
15
16
# File 'lib/rich/color_triplet.rb', line 14

def blue
  @blue
end

#greenInteger (readonly)

Returns Green component (0-255).

Returns:

  • (Integer)

    Green component (0-255)



11
12
13
# File 'lib/rich/color_triplet.rb', line 11

def green
  @green
end

#redInteger (readonly)

Returns Red component (0-255).

Returns:

  • (Integer)

    Red component (0-255)



8
9
10
# File 'lib/rich/color_triplet.rb', line 8

def red
  @red
end

Class Method Details

.from_hex(hex_str) ⇒ ColorTriplet

Create from hex string

Parameters:

  • hex_str (String)

    Hex color string (e.g., “#ff0000” or “ff0000”)

Returns:

Raises:

  • (ArgumentError)


166
167
168
169
170
171
172
173
174
175
# File 'lib/rich/color_triplet.rb', line 166

def from_hex(hex_str)
  hex_str = hex_str.delete_prefix("#")
  raise ArgumentError, "Invalid hex color: #{hex_str}" unless hex_str.match?(/\A[0-9a-fA-F]{6}\z/)

  r = hex_str[0, 2].to_i(16)
  g = hex_str[2, 2].to_i(16)
  b = hex_str[4, 2].to_i(16)

  new(r, g, b)
end

.from_hsl(h, s, l) ⇒ ColorTriplet

Create from HSL values

Parameters:

  • h (Float)

    Hue (0-360)

  • s (Float)

    Saturation (0-100)

  • l (Float)

    Lightness (0-100)

Returns:



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/rich/color_triplet.rb', line 195

def from_hsl(h, s, l)
  h = h % 360
  s = s / 100.0
  l = l / 100.0

  c = (1 - (2 * l - 1).abs) * s
  x = c * (1 - ((h / 60.0) % 2 - 1).abs)
  m = l - c / 2.0

  r, g, b = case (h / 60).floor
            when 0 then [c, x, 0]
            when 1 then [x, c, 0]
            when 2 then [0, c, x]
            when 3 then [0, x, c]
            when 4 then [x, 0, c]
            else [c, 0, x]
            end

  new(
    ((r + m) * 255).round,
    ((g + m) * 255).round,
    ((b + m) * 255).round
  )
end

.from_normalized(r, g, b) ⇒ ColorTriplet

Create from normalized values (0.0-1.0)

Parameters:

  • r (Float)

    Red component (0.0-1.0)

  • g (Float)

    Green component (0.0-1.0)

  • b (Float)

    Blue component (0.0-1.0)

Returns:



182
183
184
185
186
187
188
# File 'lib/rich/color_triplet.rb', line 182

def from_normalized(r, g, b)
  new(
    (r * 255).round,
    (g * 255).round,
    (b * 255).round
  )
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Check equality with another triplet

Parameters:

Returns:

  • (Boolean)


62
63
64
65
66
# File 'lib/rich/color_triplet.rb', line 62

def ==(other)
  return false unless other.is_a?(ColorTriplet)

  @red == other.red && @green == other.green && @blue == other.blue
end

#blend(other, factor = 0.5) ⇒ ColorTriplet

Blend this color with another

Parameters:

  • other (ColorTriplet)

    Color to blend with

  • factor (Float) (defaults to: 0.5)

    Blend factor (0.0 = this color, 1.0 = other color)

Returns:



124
125
126
127
128
129
130
131
132
# File 'lib/rich/color_triplet.rb', line 124

def blend(other, factor = 0.5)
  factor = [[factor, 0.0].max, 1.0].min

  new_r = (@red + (other.red - @red) * factor).round
  new_g = (@green + (other.green - @green) * factor).round
  new_b = (@blue + (other.blue - @blue) * factor).round

  ColorTriplet.new(new_r, new_g, new_b)
end

#dark?Boolean

Check if this is a “dark” color based on luminance

Returns:

  • (Boolean)


110
111
112
# File 'lib/rich/color_triplet.rb', line 110

def dark?
  luminance < 0.5
end

#deconstructArray<Integer>

Deconstruct for pattern matching

Returns:

  • (Array<Integer>)


87
88
89
# File 'lib/rich/color_triplet.rb', line 87

def deconstruct
  to_a
end

#deconstruct_keys(keys) ⇒ Hash

Deconstruct for pattern matching with keys

Parameters:

  • keys (Array<Symbol>)

Returns:

  • (Hash)


94
95
96
# File 'lib/rich/color_triplet.rb', line 94

def deconstruct_keys(keys)
  to_h.slice(*(keys || [:red, :green, :blue]))
end

#distance(other) ⇒ Float

Calculate color distance (Euclidean in RGB space)

Parameters:

Returns:

  • (Float)

    Distance value



137
138
139
140
141
142
# File 'lib/rich/color_triplet.rb', line 137

def distance(other)
  dr = @red - other.red
  dg = @green - other.green
  db = @blue - other.blue
  Math.sqrt(dr * dr + dg * dg + db * db)
end

#hashInteger

Returns Hash code for use in hash tables.

Returns:

  • (Integer)

    Hash code for use in hash tables



71
72
73
# File 'lib/rich/color_triplet.rb', line 71

def hash
  [@red, @green, @blue].hash
end

#hexString

Returns Hexadecimal representation without a leading ‘#’ (e.g., “ff0000”). Use #hex_with_hash for the “#ff0000” form.

Returns:

  • (String)

    Hexadecimal representation without a leading ‘#’ (e.g., “ff0000”). Use #hex_with_hash for the “#ff0000” form.



29
30
31
# File 'lib/rich/color_triplet.rb', line 29

def hex
  format("%02x%02x%02x", @red, @green, @blue)
end

#hex_with_hashString

Returns Hexadecimal representation with a leading ‘#’ (e.g., “#ff0000”).

Returns:

  • (String)

    Hexadecimal representation with a leading ‘#’ (e.g., “#ff0000”)



35
36
37
# File 'lib/rich/color_triplet.rb', line 35

def hex_with_hash
  "##{hex}"
end

#inspectString

Returns Inspect representation.

Returns:

  • (String)

    Inspect representation



81
82
83
# File 'lib/rich/color_triplet.rb', line 81

def inspect
  "#<Rich::ColorTriplet #{hex} (#{@red}, #{@green}, #{@blue})>"
end

#light?Boolean

Check if this is a “light” color based on luminance

Returns:

  • (Boolean)


116
117
118
# File 'lib/rich/color_triplet.rb', line 116

def light?
  !dark?
end

#luminanceFloat

Calculate the perceived luminance of the color Uses the formula for relative luminance from WCAG 2.0

Returns:

  • (Float)

    Luminance value (0.0-1.0)



101
102
103
104
105
106
# File 'lib/rich/color_triplet.rb', line 101

def luminance
  r, g, b = normalized.map do |c|
    c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055)**2.4
  end
  0.2126 * r + 0.7152 * g + 0.0722 * b
end

#normalizedArray<Float>

Returns Normalized components (0.0-1.0).

Returns:

  • (Array<Float>)

    Normalized components (0.0-1.0)



45
46
47
# File 'lib/rich/color_triplet.rb', line 45

def normalized
  [@red / 255.0, @green / 255.0, @blue / 255.0]
end

#rgbString

Returns RGB string representation (e.g., “rgb(255, 0, 0)”).

Returns:

  • (String)

    RGB string representation (e.g., “rgb(255, 0, 0)”)



40
41
42
# File 'lib/rich/color_triplet.rb', line 40

def rgb
  "rgb(#{@red}, #{@green}, #{@blue})"
end

#to_aArray<Integer>

Returns Components as array [red, green, blue].

Returns:

  • (Array<Integer>)

    Components as array [red, green, blue]



50
51
52
# File 'lib/rich/color_triplet.rb', line 50

def to_a
  [@red, @green, @blue]
end

#to_hHash

Returns Components as hash.

Returns:

  • (Hash)

    Components as hash



55
56
57
# File 'lib/rich/color_triplet.rb', line 55

def to_h
  { red: @red, green: @green, blue: @blue }
end

#to_sString

Returns String representation.

Returns:

  • (String)

    String representation



76
77
78
# File 'lib/rich/color_triplet.rb', line 76

def to_s
  hex
end

#weighted_distance(other) ⇒ Float

Calculate weighted color distance (better perceptual accuracy) Uses weighted Euclidean distance based on human color perception

Parameters:

Returns:

  • (Float)

    Weighted distance value



148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/rich/color_triplet.rb', line 148

def weighted_distance(other)
  dr = @red - other.red
  dg = @green - other.green
  db = @blue - other.blue

  # Weighted by perceptual importance (red-green component is most important)
  r_mean = (@red + other.red) / 2.0
  weight_r = 2.0 + r_mean / 256.0
  weight_g = 4.0
  weight_b = 2.0 + (255.0 - r_mean) / 256.0

  Math.sqrt(weight_r * dr * dr + weight_g * dg * dg + weight_b * db * db)
end