abachrome-float 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +3 -0
  3. data/.rubocop.yml +10 -0
  4. data/CHANGELOG.md +21 -0
  5. data/CLA.md +45 -0
  6. data/CODE-OF-CONDUCT.md +9 -0
  7. data/LICENSE +19 -0
  8. data/README.md +315 -0
  9. data/Rakefile +15 -0
  10. data/SECURITY.md +94 -0
  11. data/abachrome-float.gemspec +36 -0
  12. data/demos/ncurses/plasma.rb +124 -0
  13. data/devenv.lock +171 -0
  14. data/devenv.nix +52 -0
  15. data/devenv.yaml +8 -0
  16. data/lib/abachrome/color.rb +197 -0
  17. data/lib/abachrome/color_mixins/blend.rb +100 -0
  18. data/lib/abachrome/color_mixins/lighten.rb +90 -0
  19. data/lib/abachrome/color_mixins/spectral_mix.rb +70 -0
  20. data/lib/abachrome/color_mixins/to_colorspace.rb +107 -0
  21. data/lib/abachrome/color_mixins/to_grayscale.rb +87 -0
  22. data/lib/abachrome/color_mixins/to_lrgb.rb +121 -0
  23. data/lib/abachrome/color_mixins/to_oklab.rb +117 -0
  24. data/lib/abachrome/color_mixins/to_oklch.rb +110 -0
  25. data/lib/abachrome/color_mixins/to_srgb.rb +142 -0
  26. data/lib/abachrome/color_models/cmyk.rb +159 -0
  27. data/lib/abachrome/color_models/hsv.rb +49 -0
  28. data/lib/abachrome/color_models/lms.rb +38 -0
  29. data/lib/abachrome/color_models/oklab.rb +37 -0
  30. data/lib/abachrome/color_models/oklch.rb +91 -0
  31. data/lib/abachrome/color_models/rgb.rb +58 -0
  32. data/lib/abachrome/color_models/xyz.rb +31 -0
  33. data/lib/abachrome/color_models/yiq.rb +37 -0
  34. data/lib/abachrome/color_space.rb +199 -0
  35. data/lib/abachrome/converter.rb +117 -0
  36. data/lib/abachrome/converters/base.rb +128 -0
  37. data/lib/abachrome/converters/cmyk_to_srgb.rb +42 -0
  38. data/lib/abachrome/converters/lms_to_lrgb.rb +40 -0
  39. data/lib/abachrome/converters/lms_to_srgb.rb +27 -0
  40. data/lib/abachrome/converters/lms_to_xyz.rb +34 -0
  41. data/lib/abachrome/converters/lrgb_to_lms.rb +3 -0
  42. data/lib/abachrome/converters/lrgb_to_oklab.rb +57 -0
  43. data/lib/abachrome/converters/lrgb_to_srgb.rb +59 -0
  44. data/lib/abachrome/converters/lrgb_to_xyz.rb +33 -0
  45. data/lib/abachrome/converters/oklab_to_lms.rb +44 -0
  46. data/lib/abachrome/converters/oklab_to_lrgb.rb +71 -0
  47. data/lib/abachrome/converters/oklab_to_oklch.rb +56 -0
  48. data/lib/abachrome/converters/oklab_to_srgb.rb +46 -0
  49. data/lib/abachrome/converters/oklch_to_lrgb.rb +79 -0
  50. data/lib/abachrome/converters/oklch_to_oklab.rb +52 -0
  51. data/lib/abachrome/converters/oklch_to_srgb.rb +46 -0
  52. data/lib/abachrome/converters/oklch_to_xyz.rb +70 -0
  53. data/lib/abachrome/converters/srgb_to_cmyk.rb +64 -0
  54. data/lib/abachrome/converters/srgb_to_lrgb.rb +55 -0
  55. data/lib/abachrome/converters/srgb_to_oklab.rb +45 -0
  56. data/lib/abachrome/converters/srgb_to_oklch.rb +47 -0
  57. data/lib/abachrome/converters/srgb_to_yiq.rb +49 -0
  58. data/lib/abachrome/converters/xyz_to_lms.rb +34 -0
  59. data/lib/abachrome/converters/xyz_to_oklab.rb +42 -0
  60. data/lib/abachrome/converters/yiq_to_srgb.rb +47 -0
  61. data/lib/abachrome/gamut/base.rb +74 -0
  62. data/lib/abachrome/gamut/p3.rb +27 -0
  63. data/lib/abachrome/gamut/rec2020.rb +25 -0
  64. data/lib/abachrome/gamut/srgb.rb +49 -0
  65. data/lib/abachrome/illuminants/base.rb +35 -0
  66. data/lib/abachrome/illuminants/d50.rb +33 -0
  67. data/lib/abachrome/illuminants/d55.rb +29 -0
  68. data/lib/abachrome/illuminants/d65.rb +37 -0
  69. data/lib/abachrome/illuminants/d75.rb +29 -0
  70. data/lib/abachrome/named/css.rb +157 -0
  71. data/lib/abachrome/named/tailwind.rb +301 -0
  72. data/lib/abachrome/outputs/css.rb +119 -0
  73. data/lib/abachrome/palette.rb +244 -0
  74. data/lib/abachrome/palette_mixins/interpolate.rb +53 -0
  75. data/lib/abachrome/palette_mixins/resample.rb +61 -0
  76. data/lib/abachrome/palette_mixins/stretch_luminance.rb +72 -0
  77. data/lib/abachrome/parsers/css.rb +452 -0
  78. data/lib/abachrome/parsers/hex.rb +52 -0
  79. data/lib/abachrome/parsers/tailwind.rb +45 -0
  80. data/lib/abachrome/spectral.rb +276 -0
  81. data/lib/abachrome/to_abcd.rb +23 -0
  82. data/lib/abachrome/version.rb +7 -0
  83. data/lib/abachrome.rb +242 -0
  84. data/logo.png +0 -0
  85. data/logo.webp +0 -0
  86. data/security/assesments/2025-10-12-SECURITY_ASSESSMENT.md +53 -0
  87. data/security/vex.json +21 -0
  88. metadata +146 -0
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorMixins::ToOklch - OKLCH color space conversion functionality
4
+ #
5
+ # This mixin provides methods for converting colors to the OKLCH color space, which is a
6
+ # cylindrical representation of the OKLAB color space using lightness, chroma, and hue
7
+ # coordinates. OKLCH offers intuitive color manipulation through its polar coordinate
8
+ # system where hue is represented as an angle and chroma represents colorfulness.
9
+ #
10
+ # Key features:
11
+ # - Convert colors to OKLCH with automatic converter lookup
12
+ # - Both non-destructive (to_oklch) and destructive (to_oklch!) conversion methods
13
+ # - Direct access to OKLCH components (lightness, chroma, hue)
14
+ # - Utility methods for OKLCH array and value extraction
15
+ # - Optimized to return the same object when no conversion is needed
16
+ # - High-precision decimal arithmetic for accurate color science calculations
17
+ #
18
+ # The OKLCH color space uses three components: L (lightness), C (chroma/colorfulness),
19
+ # and h (hue angle in degrees), providing an intuitive interface for color adjustments
20
+ # that better matches human perception compared to traditional RGB-based color spaces.
21
+
22
+ module Abachrome
23
+ module ColorMixins
24
+ module ToOklch
25
+ # Converts the current color to the OKLCH color space.
26
+ #
27
+ # This method transforms the color into the perceptually uniform OKLCH color space.
28
+ # If the color is already in OKLCH, it returns itself unchanged. If the color is in
29
+ # OKLAB, it directly converts from OKLAB to OKLCH. For all other color spaces, it
30
+ # first converts to OKLAB as an intermediate step, then converts to OKLCH.
31
+ #
32
+ # @return [Abachrome::Color] A new Color object in the OKLCH color space
33
+ def to_oklch
34
+ return self if color_space.name == :oklch
35
+
36
+ if color_space.name == :oklab
37
+ Converters::OklabToOklch.convert(self)
38
+ else
39
+ # For other color spaces, convert to OKLab first
40
+ oklab_color = to_oklab
41
+ Converters::OklabToOklch.convert(oklab_color)
42
+ end
43
+ end
44
+
45
+ # Converts the color to OKLCH color space in-place.
46
+ # This method transforms the current color to OKLCH color space, modifying
47
+ # the original object instead of creating a new one. If the color is already
48
+ # in OKLCH space, no conversion is performed.
49
+ #
50
+ # @return [Abachrome::Color] self, allowing for method chaining
51
+ def to_oklch!
52
+ unless color_space.name == :oklch
53
+ oklch_color = to_oklch
54
+ @color_space = oklch_color.color_space
55
+ @coordinates = oklch_color.coordinates
56
+ end
57
+ self
58
+ end
59
+
60
+ # Returns the lightness component of the color in the OKLCH color space.
61
+ # This method provides direct access to the first coordinate of the OKLCH
62
+ # representation of the color, which represents perceptual lightness.
63
+ #
64
+ # @return [AbcDecimal] the lightness value in the OKLCH color space,
65
+ # typically in the range of 0.0 to 1.0, where 0.0 is black and 1.0 is white
66
+ def lightness
67
+ to_oklch.coordinates[0]
68
+ end
69
+
70
+ # Returns the chroma value of the color by converting it to the OKLCH color space.
71
+ # Chroma represents color intensity or saturation in the OKLCH color space.
72
+ #
73
+ # @return [AbcDecimal] The chroma value (second coordinate) from the OKLCH color space
74
+ def chroma
75
+ to_oklch.coordinates[1]
76
+ end
77
+
78
+ # Returns the hue value of the color in the OKLCH color space.
79
+ #
80
+ # @return [AbcDecimal] The hue component of the color in degrees (0-360)
81
+ # from the OKLCH color space representation.
82
+ def hue
83
+ to_oklch.coordinates[2]
84
+ end
85
+
86
+ # Returns the OKLCH coordinates of the color.
87
+ #
88
+ # @return [Array<AbcDecimal>] Array of OKLCH coordinates [lightness, chroma, hue] where:
89
+ # - lightness: perceptual lightness component (0-1)
90
+ # - chroma: colorfulness/saturation component
91
+ # - hue: hue angle in degrees (0-360)
92
+ def oklch_values
93
+ to_oklch.coordinates
94
+ end
95
+
96
+ # Returns the OKLCH coordinates of the color as an array.
97
+ #
98
+ # Converts the current color to OKLCH color space and returns its coordinates
99
+ # as an array. The OKLCH color space represents colors using Lightness,
100
+ # Chroma, and Hue components in a perceptually uniform way.
101
+ #
102
+ # @return [Array<Numeric>] An array containing the OKLCH coordinates [lightness, chroma, hue]
103
+ def oklch_array
104
+ to_oklch.coordinates
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorMixins::ToSrgb - sRGB color space conversion functionality
4
+ #
5
+ # This mixin provides methods for converting colors to the sRGB color space, which is the
6
+ # standard RGB color space used in most displays and web applications. sRGB uses gamma
7
+ # correction to better match human visual perception compared to linear RGB, making it
8
+ # ideal for display purposes and color output in digital media.
9
+ #
10
+ # Key features:
11
+ # - Convert colors to sRGB with automatic converter lookup
12
+ # - Both non-destructive (to_srgb/to_rgb) and destructive (to_srgb!/to_rgb!) conversion methods
13
+ # - Direct access to sRGB components (red, green, blue)
14
+ # - Utility methods for RGB array and hex string output
15
+ # - Optimized to return the same object when no conversion is needed
16
+ # - High-precision decimal arithmetic for accurate color science calculations
17
+ #
18
+ # The sRGB color space is the default RGB color space for web content and most consumer
19
+ # displays, providing a standardized way to represent colors that will appear consistently
20
+ # across different devices and applications.
21
+
22
+ require_relative "../converter"
23
+
24
+ module Abachrome
25
+ module ColorMixins
26
+ module ToSrgb
27
+ # Converts the current color to the sRGB color space.
28
+ #
29
+ # If the color is already in the sRGB color space, returns the color instance
30
+ # unchanged. Otherwise, performs a color space conversion from the current
31
+ # color space to sRGB.
32
+ #
33
+ # @return [Abachrome::Color] A new Color instance in the sRGB color space,
34
+ # or self if already in sRGB
35
+ def to_srgb
36
+ return self if color_space.name == :srgb
37
+
38
+ Converter.convert(self, :srgb)
39
+ end
40
+
41
+ # Alias for #to_srgb method.
42
+ #
43
+ # @return [Abachrome::Color] The color converted to sRGB color space
44
+ def to_rgb
45
+ # assume they mean srgb
46
+ to_srgb
47
+ end
48
+
49
+ # Converts the color to the sRGB color space, mutating the current object.
50
+ # If the color is already in sRGB space, this method does nothing.
51
+ # @return [Abachrome::Color] self for method chaining
52
+ def to_srgb!
53
+ unless color_space.name == :srgb
54
+ srgb_color = to_srgb
55
+ @color_space = srgb_color.color_space
56
+ @coordinates = srgb_color.coordinates
57
+ end
58
+ self
59
+ end
60
+
61
+ # Converts the current color to sRGB color space in place.
62
+ # This is an alias for {#to_srgb!} as RGB commonly refers to sRGB
63
+ # in web and design contexts.
64
+ #
65
+ # @return [self] Returns self after converting to sRGB
66
+ def to_rgb!
67
+ # assume they mean srgb
68
+ to_srgb!
69
+ end
70
+
71
+ # Returns the red component of the color in the sRGB color space.
72
+ #
73
+ # @return [AbcDecimal] The red component value in the sRGB color space,
74
+ # normalized between 0 and 1.
75
+ def red
76
+ to_srgb.coordinates[0]
77
+ end
78
+
79
+ # Returns the green component of the color in sRGB space.
80
+ #
81
+ # This method converts the current color to sRGB color space if needed,
82
+ # then extracts the green component (second coordinate).
83
+ #
84
+ # @return [AbcDecimal] The green component value in the sRGB color space, typically in the range 0-1
85
+ def green
86
+ to_srgb.coordinates[1]
87
+ end
88
+
89
+ # Returns the blue component of the color in sRGB color space.
90
+ #
91
+ # This method converts the current color to sRGB if needed and
92
+ # extracts the third coordinate value (blue).
93
+ #
94
+ # @return [AbcDecimal] The blue component value in sRGB space, typically in range 0-1
95
+ def blue
96
+ to_srgb.coordinates[2]
97
+ end
98
+
99
+ # Returns the RGB color values in the sRGB color space.
100
+ #
101
+ # @return [Array<AbcDecimal>] An array of three AbcDecimal values representing
102
+ # the red, green, and blue color components in the sRGB color space.
103
+ def srgb_values
104
+ to_srgb.coordinates
105
+ end
106
+
107
+ # Returns the RGB values of the color as coordinates in the sRGB color space.
108
+ #
109
+ # @return [Array<Abachrome::AbcDecimal>] The RGB coordinates (red, green, blue) in sRGB color space
110
+ def rgb_values
111
+ to_srgb.coordinates
112
+ end
113
+
114
+ # Returns an array of RGB values (0-255) for this color.
115
+ #
116
+ # This method converts the color to sRGB, then scales the component values
117
+ # from the 0-1 range to the 0-255 range commonly used in RGB color codes.
118
+ # Values are rounded to the nearest integer and clamped between 0 and 255.
119
+ #
120
+ # @return [Array<Integer>] An array of three integers representing the [R, G, B]
121
+ # values in the 0-255 range
122
+ def rgb_array
123
+ to_srgb.coordinates.map { |c| (c * 255).round.clamp(0, 255) }
124
+ end
125
+
126
+ # Returns a hexadecimal representation of this color in sRGB color space.
127
+ # Converts the color to sRGB, then formats it as a hexadecimal string.
128
+ #
129
+ # @return [String] A string in the format "#RRGGBB" where RR, GG, and BB are
130
+ # the hexadecimal representations of the red, green, and blue components,
131
+ # each ranging from 00 to FF.
132
+ # @example
133
+ # color.rgb_hex #=> "#3a7bc8"
134
+ def rgb_hex
135
+ r, g, b = rgb_array
136
+ format("#%02x%02x%02x", r, g, b)
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorModels::CMYK - CMYK color space model utilities
4
+ #
5
+ # This module provides utility methods for the CMYK (Cyan, Magenta, Yellow, Key/Black)
6
+ # color model within the Abachrome color manipulation library. CMYK represents colors
7
+ # using the subtractive color synthesis model used in printing and physical media.
8
+ #
9
+ # Key features:
10
+ # - Subtractive color model (ink on paper, not light)
11
+ # - Four channels: Cyan, Magenta, Yellow, and Key (Black)
12
+ # - Supports Undercolor Removal (UCR) for ink savings
13
+ # - Supports Gray Component Replacement (GCR) for richer blacks
14
+ # - High-precision BigDecimal arithmetic for exact ink percentages
15
+ # - Essential for print media, PDF generation, and pre-press workflows
16
+ #
17
+ # The CMYK model is fundamental for professional printing, where the fourth channel
18
+ # (Black/Key) is added to achieve crisp text and deep shadows that cannot be produced
19
+ # by combining cyan, magenta, and yellow inks alone.
20
+
21
+
22
+ module Abachrome
23
+ module ColorModels
24
+ class CMYK
25
+ include Abachrome::ToAbcd
26
+
27
+ class << self
28
+ # Normalizes CMYK color component values to the [0,1] range.
29
+ #
30
+ # @param c [Numeric] Cyan component (0-1 or 0-100 for percentages)
31
+ # @param m [Numeric] Magenta component (0-1 or 0-100 for percentages)
32
+ # @param y [Numeric] Yellow component (0-1 or 0-100 for percentages)
33
+ # @param k [Numeric] Key/Black component (0-1 or 0-100 for percentages)
34
+ # @return [Array<AbcDecimal>] Array of four normalized components as AbcDecimal objects
35
+ def normalize(c, m, y, k)
36
+ [c, m, y, k].map do |value|
37
+ case value
38
+ when String
39
+ if value.end_with?("%")
40
+ value_without_percent = value.chomp("%")
41
+ value_without_percent.to_f / 100.to_f
42
+ else
43
+ value.to_f
44
+ end
45
+ when Numeric
46
+ if value > 1
47
+ value.to_f / 100.to_f
48
+ else
49
+ value.to_f
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # Converts RGB values to CMYK using the standard naive conversion.
56
+ # This produces high ink coverage and is generally not recommended for production.
57
+ #
58
+ # @param r [Numeric] Red component (0-1)
59
+ # @param g [Numeric] Green component (0-1)
60
+ # @param b [Numeric] Blue component (0-1)
61
+ # @return [Array<AbcDecimal>] Array of [c, m, y, k] values
62
+ def from_rgb_naive(r, g, b)
63
+ r = r.to_f
64
+ g = g.to_f
65
+ b = b.to_f
66
+
67
+ # Simple complementary conversion
68
+ c = 1.to_f - r
69
+ m = 1.to_f - g
70
+ y = 1.to_f - b
71
+ k = 0.to_f
72
+
73
+ [c, m, y, k]
74
+ end
75
+
76
+ # Converts RGB values to CMYK using Undercolor Removal (UCR).
77
+ # This extracts the gray component to the black (K) channel, reducing ink usage.
78
+ #
79
+ # @param r [Numeric] Red component (0-1)
80
+ # @param g [Numeric] Green component (0-1)
81
+ # @param b [Numeric] Blue component (0-1)
82
+ # @param gcr_amount [Numeric] Gray Component Replacement amount (0-1), default 1.0 for full UCR
83
+ # @return [Array<AbcDecimal>] Array of [c, m, y, k] values
84
+ def from_rgb_ucr(r, g, b, gcr_amount = 1.0)
85
+ r = r.to_f
86
+ g = g.to_f
87
+ b = b.to_f
88
+ gcr = gcr_amount.to_f
89
+
90
+ # Calculate complementary CMY values
91
+ c = 1.to_f - r
92
+ m = 1.to_f - g
93
+ y = 1.to_f - b
94
+
95
+ # Find the minimum (gray component)
96
+ k = [c, m, y].min * gcr
97
+
98
+ # Remove the gray component from CMY if k > 0
99
+ if k.positive?
100
+ c -= k
101
+ m -= k
102
+ y -= k
103
+ end
104
+
105
+ [c, m, y, k]
106
+ end
107
+
108
+ # Alias for from_rgb_ucr with full GCR (the standard approach).
109
+ # GCR is essentially the same as UCR but allows control over how much
110
+ # of the gray component to extract.
111
+ #
112
+ # @param r [Numeric] Red component (0-1)
113
+ # @param g [Numeric] Green component (0-1)
114
+ # @param b [Numeric] Blue component (0-1)
115
+ # @param amount [Numeric] Amount of GCR to apply (0-1), default 1.0
116
+ # @return [Array<AbcDecimal>] Array of [c, m, y, k] values
117
+ def from_rgb_gcr(r, g, b, amount = 1.0)
118
+ from_rgb_ucr(r, g, b, amount)
119
+ end
120
+
121
+ # Converts CMYK values back to RGB.
122
+ # This is a straightforward inverse of the naive RGB→CMY conversion
123
+ # plus the addition of the black channel.
124
+ #
125
+ # @param c [Numeric] Cyan component (0-1)
126
+ # @param m [Numeric] Magenta component (0-1)
127
+ # @param y [Numeric] Yellow component (0-1)
128
+ # @param k [Numeric] Key/Black component (0-1)
129
+ # @return [Array<AbcDecimal>] Array of [r, g, b] values
130
+ def to_rgb(c, m, y, k)
131
+ c = c.to_f
132
+ m = m.to_f
133
+ y = y.to_f
134
+ k = k.to_f
135
+
136
+ # Standard CMYK to RGB conversion
137
+ r = (1.to_f - c) * (1.to_f - k)
138
+ g = (1.to_f - m) * (1.to_f - k)
139
+ b = (1.to_f - y) * (1.to_f - k)
140
+
141
+ [r, g, b]
142
+ end
143
+
144
+ # Calculates the Total Area Coverage (TAC) for CMYK values.
145
+ # TAC is the sum of all four ink percentages and is critical in print workflows
146
+ # to prevent excessive ink that can cause smearing or paper damage.
147
+ #
148
+ # @param c [Numeric] Cyan component (0-1)
149
+ # @param m [Numeric] Magenta component (0-1)
150
+ # @param y [Numeric] Yellow component (0-1)
151
+ # @param k [Numeric] Key/Black component (0-1)
152
+ # @return [AbcDecimal] Total ink coverage as a decimal (0-4, often expressed as 0-400%)
153
+ def total_area_coverage(c, m, y, k)
154
+ c.to_f + m.to_f + y.to_f + k.to_f
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorModels::HSV - HSV color space model definition
4
+ #
5
+ # This module defines the HSV (Hue, Saturation, Value) color model within the Abachrome
6
+ # color manipulation library. HSV provides an intuitive way to represent colors using
7
+ # cylindrical coordinates where hue represents the color type, saturation represents
8
+ # the intensity or purity of the color, and value represents the brightness.
9
+ #
10
+ # Key features:
11
+ # - Registers the HSV color space with coordinate names [hue, saturation, value]
12
+ # - Validates HSV coordinates to ensure all components are within the [0, 1] range
13
+ # - Uses normalized 0-1 values internally for consistency with other color models
14
+ # - Provides a more intuitive interface for color adjustments compared to RGB
15
+ # - Supports conversion to and from other color spaces through the converter system
16
+ #
17
+ # The HSV model is particularly useful for color picking interfaces and applications
18
+ # where users need to adjust color properties in a way that matches human perception
19
+ # of color relationships. All coordinate values are stored as AbcDecimal objects to
20
+ # maintain precision during color science calculations.
21
+
22
+ module Abachrome
23
+ module ColorModels
24
+ class HSV < Base
25
+ #
26
+ # Internally, we use 0..1.0 values for hsv, unlike the standard 0..360, 0..255, 0..255.
27
+ #
28
+ # Values can be converted for output.
29
+ #
30
+
31
+ register :hsv, "HSV", %w[hue saturation value]
32
+
33
+ # Validates whether the coordinates are valid for the HSV color model.
34
+ # Each component (hue, saturation, value) must be in the range [0, 1].
35
+ #
36
+ # @param coordinates [Array<Numeric>] An array of three values representing
37
+ # hue (h), saturation (s), and value (v) in the range [0, 1]
38
+ # @return [Boolean] true if all coordinates are within valid ranges, false otherwise
39
+ def valid_coordinates?(coordinates)
40
+ h, s, v = coordinates
41
+ h.between?(0, 1.0) &&
42
+ s >= 0 && s <= 1.0 &&
43
+ v >= 0 && v <= 1.0
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorModels::Lms - LMS color space model definition
4
+ #
5
+ # This module defines the LMS color model within the Abachrome color manipulation library.
6
+ # LMS represents the response of the three types of cone cells in the human eye (Long, Medium, Short)
7
+ # and serves as an intermediate color space in the OKLAB transformation pipeline. The LMS color space
8
+ # provides a foundation for perceptually uniform color representations by modeling human visual perception
9
+ # at the photoreceptor level.
10
+ #
11
+ # Key features:
12
+ # - Registers the LMS color space with coordinate names [l, m, s]
13
+ # - Represents cone cell responses for Long, Medium, and Short wavelength sensitivity
14
+ # - Serves as intermediate color space for OKLAB and linear RGB conversions
15
+ # - Uses normalized values for consistency with other color models in the library
16
+ # - Maintains high precision through AbcDecimal arithmetic for color transformations
17
+ # - Provides validation for LMS coordinate ranges to ensure valid color representations
18
+ #
19
+ # The LMS model is particularly important in the color science pipeline as it bridges the gap
20
+ # between linear RGB representations and perceptually uniform color spaces like OKLAB, enabling
21
+ # accurate color transformations that better match human visual perception characteristics.
22
+
23
+ module Abachrome
24
+ module ColorModels
25
+ class Lms
26
+ end
27
+ end
28
+ end
29
+
30
+ ColorSpace.register(
31
+ :lms,
32
+ "LMS",
33
+ %w[l m s],
34
+ nil,
35
+ []
36
+ )
37
+
38
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorModels::Oklab - OKLAB color space model definition
4
+ #
5
+ # This module defines the OKLAB color model within the Abachrome color manipulation library.
6
+ # OKLAB is a perceptually uniform color space designed for better color manipulation and
7
+ # comparison, providing more intuitive lightness adjustments and color blending compared
8
+ # to traditional RGB color spaces.
9
+ #
10
+ # Key features:
11
+ # - Registers the OKLAB color space with coordinate names [lightness, a, b]
12
+ # - Uses L (lightness) ranging from 0 (black) to 1 (white)
13
+ # - Uses a and b components representing green-red and blue-yellow axes respectively
14
+ # - Provides perceptually uniform color space for accurate color science calculations
15
+ # - Serves as an intermediate color space for conversions to OKLCH and other models
16
+ # - Maintains high precision through AbcDecimal arithmetic for color transformations
17
+ #
18
+ # The OKLAB model is particularly useful for color adjustments that need to appear natural
19
+ # to human perception, such as lightness modifications, color blending, and gamut mapping
20
+ # operations where perceptual uniformity is important.
21
+
22
+ module Abachrome
23
+ module ColorModels
24
+ class Oklab
25
+ end
26
+ end
27
+ end
28
+
29
+ ColorSpace.register(
30
+ :oklab,
31
+ "Oklab",
32
+ %w[l a b],
33
+ nil,
34
+ ["ok-lab"]
35
+ )
36
+
37
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorModels::Oklch - OKLCH color space model definition
4
+ #
5
+ # This module defines the OKLCH color model within the Abachrome color manipulation library.
6
+ # OKLCH is a cylindrical representation of the OKLAB color space using lightness, chroma, and hue
7
+ # coordinates. OKLCH offers intuitive color manipulation through its polar coordinate
8
+ # system where hue is represented as an angle and chroma represents colorfulness.
9
+ #
10
+ # Key features:
11
+ # - Registers the OKLCH color space with coordinate names [lightness, chroma, hue]
12
+ # - Provides conversion utilities between OKLCH and OKLAB color spaces
13
+ # - Uses L (lightness) ranging from 0 (black) to 1 (white)
14
+ # - Uses C (chroma) representing colorfulness intensity starting from 0
15
+ # - Uses h (hue) as an angle in degrees from 0 to 360
16
+ # - Includes normalization methods for proper coordinate ranges
17
+ # - Maintains high precision through AbcDecimal arithmetic for color transformations
18
+ #
19
+ # The OKLCH model is particularly useful for color adjustments that need to appear natural
20
+ # to human perception, such as hue shifts, saturation modifications, and lightness changes
21
+ # where the cylindrical coordinate system provides more intuitive control compared to
22
+ # rectangular color spaces.
23
+
24
+ module Abachrome
25
+ module ColorModels
26
+ class Oklch
27
+ # Normalizes OKLCH color values to their standard ranges.
28
+ #
29
+ # @param l [Numeric] The lightness component, will be clamped to range 0-1
30
+ # @param c [Numeric] The chroma component, will be clamped to range 0-1
31
+ # @param h [Numeric] The hue component in degrees, will be normalized to range 0-360
32
+ # @return [Array<AbcDecimal>] Array containing the normalized [lightness, chroma, hue] values
33
+ def self.normalize(l, c, h)
34
+ l = l.to_f
35
+ c = c.to_f
36
+ h = h.to_f
37
+
38
+ # Normalize hue to 0-360 range
39
+ h -= 360 while h >= 360
40
+ h += 360 while h.negative?
41
+
42
+ # Normalize lightness and chroma to 0-1 range
43
+ l = l.clamp(0, 1)
44
+ c = c.clamp(0, 1)
45
+
46
+ [l, c, h]
47
+ end
48
+
49
+ # Converts OKLCH color coordinates to OKLab color coordinates.
50
+ #
51
+ # @param l [Numeric] The lightness value in the OKLCH color space
52
+ # @param c [Numeric] The chroma value in the OKLCH color space
53
+ # @param h [Numeric] The hue value in degrees in the OKLCH color space
54
+ # @return [Array<Numeric>] An array containing the OKLab coordinates [l, a, b]
55
+ def self.to_oklab(l, c, h)
56
+ # Convert OKLCH to OKLab
57
+ h_rad = h * Math::PI / 180
58
+ a = c * Math.cos(h_rad)
59
+ b = c * Math.sin(h_rad)
60
+ [l, a, b]
61
+ end
62
+
63
+ # Converts OKLab color coordinates to OKLCH color coordinates.
64
+ #
65
+ # @param l [Numeric] The lightness component from OKLab.
66
+ # @param a [Numeric] The green-red component from OKLab.
67
+ # @param b [Numeric] The blue-yellow component from OKLab.
68
+ # @return [Array<Numeric>] An array containing the OKLCH values [l, c, h] where:
69
+ # - l is the lightness component (unchanged from OKLab)
70
+ # - c is the chroma component (calculated from a and b)
71
+ # - h is the hue angle in degrees (0-360)
72
+ def self.from_oklab(l, a, b)
73
+ # Convert OKLab to OKLCH
74
+ c = Math.sqrt((a * a) + (b * b))
75
+ h = Math.atan2(b, a) * 180 / Math::PI
76
+ h += 360 if h.negative?
77
+ [l, c, h]
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ ColorSpace.register(
84
+ :oklch,
85
+ "OKLCh",
86
+ %w[lightness chroma hue],
87
+ nil,
88
+ ["ok-lch"]
89
+ )
90
+
91
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abachrome::ColorModels::RGB - RGB color space model utilities
4
+ #
5
+ # This module provides utility methods for the RGB color model within the Abachrome
6
+ # color manipulation library. RGB represents colors using red, green, and blue
7
+ # components, which is the most common color model for digital displays and web
8
+ # applications.
9
+ #
10
+ # Key features:
11
+ # - Normalizes RGB component values to the [0, 1] range from various input formats
12
+ # - Handles percentage values (with % suffix) by dividing by 100
13
+ # - Handles 0-255 range values by dividing by 255
14
+ # - Handles 0-1 range values directly without conversion
15
+ # - Supports string and numeric input types for flexible color specification
16
+ # - Maintains high precision through AbcDecimal arithmetic for color calculations
17
+ #
18
+ # The RGB model serves as a foundation for sRGB and linear RGB color spaces,
19
+ # providing the basic coordinate normalization needed for accurate color
20
+ # representation and conversion between different RGB-based color spaces.
21
+
22
+ module Abachrome
23
+ module ColorModels
24
+ class RGB
25
+ class << self
26
+ # Normalizes RGB color component values to the [0,1] range.
27
+ #
28
+ # @param r [String, Numeric] Red component. If string with % suffix, interpreted as percentage;
29
+ # if string without suffix or numeric > 1, interpreted as 0-255 range value;
30
+ # if numeric ≤ 1, used directly.
31
+ # @param g [String, Numeric] Green component. Same interpretation as red component.
32
+ # @param b [String, Numeric] Blue component. Same interpretation as red component.
33
+ # @return [Array<AbcDecimal>] Array of three normalized components as AbcDecimal objects,
34
+ # each in the range [0,1].
35
+ def normalize(r, g, b)
36
+ [r, g, b].map do |value|
37
+ case value
38
+ when String
39
+ if value.end_with?("%")
40
+ value.chomp("%".to_f) / 100.to_f
41
+ else
42
+ value.to_f / 255.to_f
43
+ end
44
+ when Numeric
45
+ if value > 1
46
+ value.to_f / 255.to_f
47
+ else
48
+ value.to_f
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # Copyright (c) 2025 Durable Programming, LLC. All rights reserved.