module Color
def ==(other)
All values are compared as floating-point values, so two colors will be reported
approximate than non-converted comparisons.
type as the current color. Such converted color comparisons will always be more
Compares the `other` color to this one. The `other` color will be coerced to the same
#
def ==(other) other.is_a?(Color) && to_internal.zip(coerce(other).to_internal).all? { near?(_1, _2) } end
def components = 3 # :nodoc:
(names).
for Color::RGB _regardless_ because there's an additional member for RGB colors
We _could_ define this as `members.count`, but this would require a special case
require more or less should override this.
defined with three components, we define a constant value here. Color classes that
It is useful to know the number of components in some cases. Since most colors are
#
def components = 3 # :nodoc:
def css_value(value, format = nil) # :nodoc:
#
def css_value(value, format = nil) # :nodoc: if value.nil? "none" elsif near_zero?(value) "0" else suffix = case format in :percent "%" in :degrees "deg" else "" end "%3.2f%s" % [value, suffix] end end
def from_internal(...) = self.class.from_internal(...)
def from_internal(...) = self.class.from_internal(...)
def map(&block) = self.class.from_internal(*to_internal.map(&block))
Apply the provided block to each color component in turn, returning a new color
#
def map(&block) = self.class.from_internal(*to_internal.map(&block))
def map_with(other, &block) = self.class.from_internal(*zip(other).map(&block))
Apply the provided block to the color component pairs in turn, returning a new color
#
def map_with(other, &block) = self.class.from_internal(*zip(other).map(&block))
def near?(x, y) = (x - y).abs <= Color::TOLERANCE # :nodoc:
Returns +true+ if the two values provided are near each other.
#
def near?(x, y) = (x - y).abs <= Color::TOLERANCE # :nodoc:
def near_one?(value) = near_zero?(value - 1.0) # :nodoc:
Returns +true+ if the value is within EPSILON of one.
#
def near_one?(value) = near_zero?(value - 1.0) # :nodoc:
def near_one_or_more?(value) = (value > 1.0 or near_one?(value)) # :nodoc:
Returns +true+ if the value is within EPSILON of one or more than one.
#
def near_one_or_more?(value) = (value > 1.0 or near_one?(value)) # :nodoc:
def near_zero?(value) = (value.abs <= Color::EPSILON) # :nodoc:
Returns `true` if the value is less than EPSILON.
#
def near_zero?(value) = (value.abs <= Color::EPSILON) # :nodoc:
def near_zero_or_less?(value) = (value < 0.0 or near_zero?(value)) # :nodoc:
Returns `true` if the value is within EPSILON of zero or less than zero.
#
def near_zero_or_less?(value) = (value < 0.0 or near_zero?(value)) # :nodoc:
def normalize(value, range = 0.0..1.0) # :nodoc:
Normalizes the value to the range (0.0) .. (1.0).
#
def normalize(value, range = 0.0..1.0) # :nodoc: clamp(range) , range.begin) lue, range.end)
def normalize_byte(value) = normalize_to_range(value, 0..255).to_i # :nodoc:
Normalize the value to the range (0) .. (255).
#
def normalize_byte(value) = normalize_to_range(value, 0..255).to_i # :nodoc:
def normalize_to_range(value, range) # :nodoc:
Normalizes the value to the specified range.
#
def normalize_to_range(value, range) # :nodoc: range = (range.end..range.begin) if range.end < range.begin if value <= range.begin range.begin elsif value >= range.end range.end else value end end
def normalize_word(value) = normalize_to_range(value, 0..65535).to_i # :nodoc:
Normalize the value to the range (0) .. (65535).
#
def normalize_word(value) = normalize_to_range(value, 0..65535).to_i # :nodoc:
def scale(*factors)
rgb.scale(1, 2) # => Invalid scaling factors [1, 2] for Color::RGB (ArgumentError)
rgb.scale(0, 0.5, 2) # => RGB [#006fff]
# 0xf5 * 0 == 0x00, 0xde * 0.5 == 0x6f, 0xb3 * 2 == 0x166 (clamped to 0xff)
rgb = Color::RGB::Wheat # => RGB [#f5deb3]
```ruby
color component of the color object or an `ArgumentError` will be raised.
If more than one scaling factor is provided, there must be exactly one factor for each
```
rgb.scale(0.75) # => RGB [#b8a786]
rgb = Color::RGB::Wheat # => RGB [#f5deb3]
```ruby
If a single scaling factor is provided, it is applied to all components:
color object with the scaled values.
Multiplies each component value by the scaling factor or factors, returning a new
#
def scale(*factors) if factors.size == 1 factor = factors.first map { _1 * factor } elsif factors.size != components raise ArgumentError, "Invalid scaling factors #{factors.inspect} for #{self.class}" else new_components = to_internal.zip(factors).map { _1 * _2 } self.class.from_internal(*new_components) end end
def to_degrees(radians) # :nodoc:
#
def to_degrees(radians) # :nodoc: if radians < 0 (Math::PI + radians % -Math::PI) * (180 / Math::PI) + 180 else (radians % Math::PI) * (180 / Math::PI) end end
def to_radians(degrees) # :nodoc:
#
def to_radians(degrees) # :nodoc: degrees = ((degrees % 360) + 360) % 360 if degrees >= 180 Math::PI * (degrees - 360) / 180.0 else Math::PI * degrees / 180.0 end end
def translate_range(x, to:, from: 0.0..1.0) # :nodoc:
The value is clamped to the values of `to`.
y = (((x - a) * (d - c)) / (b - a)) + c
[c, d] ← to ← [to.begin, to.end]
[a, b] ← from ← [from.begin, from.end]
This is based on the formula:
As 0.0 .. 1.0 is a common internal range, it is the default for `from`.
Translates a value from range `from` to range `to`. Both ranges must be closed.
#
def translate_range(x, to:, from: 0.0..1.0) # :nodoc: egin, from.end] in, to.end] * (d - c)) / (b - a)) + c
def zip(other) = to_internal.zip(coerce(other).to_internal)
#
def zip(other) = to_internal.zip(coerce(other).to_internal)