module Color

def ==(other)

equivalent if all component values are within +TOLERANCE+ of each 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:

: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:

: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))

instance.
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))

instance.
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:

: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:

: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:

: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:

: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:

: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:

: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:

: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:

: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:

: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:

: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:

: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:

: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)

Zip the color component pairs together.
#
def zip(other) = to_internal.zip(coerce(other).to_internal)