abachrome 0.1.5 → 0.2.0
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.
- checksums.yaml +4 -4
- data/abachrome.gemspec +1 -0
- data/devenv.nix +1 -1
- data/lib/abachrome/abc_decimal.rb +42 -35
- data/lib/abachrome/color.rb +61 -10
- data/lib/abachrome/color_mixins/blend.rb +7 -5
- data/lib/abachrome/color_mixins/harmonies.rb +187 -0
- data/lib/abachrome/color_mixins/lighten.rb +8 -6
- data/lib/abachrome/color_mixins/spectral_mix.rb +70 -0
- data/lib/abachrome/color_mixins/to_colorspace.rb +10 -8
- data/lib/abachrome/color_mixins/to_grayscale.rb +87 -0
- data/lib/abachrome/color_mixins/to_lms.rb +106 -0
- data/lib/abachrome/color_mixins/to_lrgb.rb +14 -12
- data/lib/abachrome/color_mixins/to_oklab.rb +16 -14
- data/lib/abachrome/color_mixins/to_oklch.rb +12 -10
- data/lib/abachrome/color_mixins/to_srgb.rb +17 -15
- data/lib/abachrome/color_mixins/to_xyz.rb +106 -0
- data/lib/abachrome/color_mixins/wcag.rb +126 -0
- data/lib/abachrome/color_models/cmyk.rb +160 -0
- data/lib/abachrome/color_models/hsv.rb +5 -3
- data/lib/abachrome/color_models/lms.rb +3 -1
- data/lib/abachrome/color_models/oklab.rb +3 -1
- data/lib/abachrome/color_models/oklch.rb +6 -4
- data/lib/abachrome/color_models/rgb.rb +4 -2
- data/lib/abachrome/color_models/xyz.rb +11 -1
- data/lib/abachrome/color_models/yiq.rb +37 -0
- data/lib/abachrome/color_space.rb +28 -14
- data/lib/abachrome/converter.rb +10 -8
- data/lib/abachrome/converters/base.rb +13 -11
- data/lib/abachrome/converters/cmyk_to_srgb.rb +42 -0
- data/lib/abachrome/converters/lms_to_lrgb.rb +5 -3
- data/lib/abachrome/converters/lms_to_oklab.rb +28 -0
- data/lib/abachrome/converters/lms_to_srgb.rb +6 -4
- data/lib/abachrome/converters/lms_to_xyz.rb +5 -3
- data/lib/abachrome/converters/lrgb_to_lms.rb +29 -1
- data/lib/abachrome/converters/lrgb_to_oklab.rb +5 -3
- data/lib/abachrome/converters/lrgb_to_srgb.rb +6 -4
- data/lib/abachrome/converters/lrgb_to_xyz.rb +5 -3
- data/lib/abachrome/converters/oklab_to_lms.rb +9 -7
- data/lib/abachrome/converters/oklab_to_lrgb.rb +7 -7
- data/lib/abachrome/converters/oklab_to_oklch.rb +4 -2
- data/lib/abachrome/converters/oklab_to_srgb.rb +4 -2
- data/lib/abachrome/converters/oklab_to_xyz.rb +29 -0
- data/lib/abachrome/converters/oklch_to_lms.rb +28 -0
- data/lib/abachrome/converters/oklch_to_lrgb.rb +5 -3
- data/lib/abachrome/converters/oklch_to_oklab.rb +5 -3
- data/lib/abachrome/converters/oklch_to_srgb.rb +6 -4
- data/lib/abachrome/converters/oklch_to_xyz.rb +6 -4
- data/lib/abachrome/converters/srgb_to_cmyk.rb +64 -0
- data/lib/abachrome/converters/srgb_to_lms.rb +29 -0
- data/lib/abachrome/converters/srgb_to_lrgb.rb +5 -3
- data/lib/abachrome/converters/srgb_to_oklab.rb +4 -2
- data/lib/abachrome/converters/srgb_to_oklch.rb +5 -3
- data/lib/abachrome/converters/srgb_to_xyz.rb +30 -0
- data/lib/abachrome/converters/srgb_to_yiq.rb +49 -0
- data/lib/abachrome/converters/xyz_to_lms.rb +5 -3
- data/lib/abachrome/converters/xyz_to_lrgb.rb +33 -0
- data/lib/abachrome/converters/xyz_to_oklab.rb +5 -3
- data/lib/abachrome/converters/xyz_to_srgb.rb +30 -0
- data/lib/abachrome/converters/yiq_to_srgb.rb +47 -0
- data/lib/abachrome/floatify.rb +282 -0
- data/lib/abachrome/gamut/base.rb +3 -3
- data/lib/abachrome/gamut/p3.rb +3 -3
- data/lib/abachrome/gamut/rec2020.rb +2 -2
- data/lib/abachrome/gamut/srgb.rb +4 -2
- data/lib/abachrome/illuminants/base.rb +2 -2
- data/lib/abachrome/illuminants/d50.rb +2 -2
- data/lib/abachrome/illuminants/d55.rb +2 -2
- data/lib/abachrome/illuminants/d65.rb +2 -2
- data/lib/abachrome/illuminants/d75.rb +2 -2
- data/lib/abachrome/named/css.rb +149 -149
- data/lib/abachrome/named/tailwind.rb +265 -265
- data/lib/abachrome/outputs/css.rb +2 -2
- data/lib/abachrome/palette.rb +26 -25
- data/lib/abachrome/palette_mixins/interpolate.rb +3 -1
- data/lib/abachrome/palette_mixins/resample.rb +2 -2
- data/lib/abachrome/palette_mixins/stretch_luminance.rb +2 -2
- data/lib/abachrome/parsers/css.rb +86 -71
- data/lib/abachrome/parsers/hex.rb +2 -2
- data/lib/abachrome/parsers/tailwind.rb +8 -8
- data/lib/abachrome/spectral.rb +277 -0
- data/lib/abachrome/to_abcd.rb +4 -4
- data/lib/abachrome/version.rb +2 -2
- data/lib/abachrome.rb +66 -10
- metadata +41 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: adfda90185a392dcd8859acac1f53b7ac4d9ace7a083b1b643baeeea49de76b8
|
|
4
|
+
data.tar.gz: b7b66f5e35f5f7f16b1704fced9de7cb85b2088b98afaf6c9aa0f31ad8e4fdb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: adaebc43ce61184e91852927c83f371234fdde03aeff491570b35146d0f899bb7919336c0f5908c23d80b8233c1a9327a7d8fe5a54da83eac1b32a3d04c7d26e
|
|
7
|
+
data.tar.gz: 4088bd45b14da9b2d7c475a07cd9ee5797df50abe03701f48f64f7c684edaac8b24736a53b0d49aac5fca88d4e45b21535c86fbfd927e7d2095d11b515e57ed8
|
data/abachrome.gemspec
CHANGED
data/devenv.nix
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::AbcDecimal - High-precision decimal arithmetic for color calculations
|
|
2
4
|
#
|
|
3
5
|
# This class provides a wrapper around Ruby's BigDecimal to ensure consistent precision
|
|
@@ -19,6 +21,7 @@ require "forwardable"
|
|
|
19
21
|
module Abachrome
|
|
20
22
|
class AbcDecimal
|
|
21
23
|
extend Forwardable
|
|
24
|
+
|
|
22
25
|
DEFAULT_PRECISION = (ENV["ABC_DECIMAL_PRECISION"] || "24").to_i
|
|
23
26
|
|
|
24
27
|
attr_accessor :value, :precision
|
|
@@ -26,7 +29,7 @@ module Abachrome
|
|
|
26
29
|
def_delegators :@value, :to_i, :zero?, :nonzero?, :finite?
|
|
27
30
|
|
|
28
31
|
# Initializes a new AbcDecimal object with the specified value and precision.
|
|
29
|
-
#
|
|
32
|
+
#
|
|
30
33
|
# @param value [AbcDecimal, BigDecimal, Rational, #to_s] The numeric value to represent.
|
|
31
34
|
# If an AbcDecimal is provided, its internal value is used.
|
|
32
35
|
# If a BigDecimal or Rational is provided, it's used directly.
|
|
@@ -49,11 +52,11 @@ module Abachrome
|
|
|
49
52
|
end
|
|
50
53
|
|
|
51
54
|
# Returns a string representation of the decimal value.
|
|
52
|
-
#
|
|
55
|
+
#
|
|
53
56
|
# This method converts the internal value to a String, using a fixed-point
|
|
54
57
|
# notation format. If the internal value is a Rational, it's first converted
|
|
55
58
|
# to a BigDecimal with the configured precision before string conversion.
|
|
56
|
-
#
|
|
59
|
+
#
|
|
57
60
|
# @return [String] The decimal value as a string in fixed-point notation
|
|
58
61
|
def to_s
|
|
59
62
|
if @value.is_a?(Rational)
|
|
@@ -64,14 +67,14 @@ module Abachrome
|
|
|
64
67
|
end
|
|
65
68
|
|
|
66
69
|
# Converts the decimal value to a floating-point number.
|
|
67
|
-
#
|
|
70
|
+
#
|
|
68
71
|
# @return [Float] the floating-point representation of the AbcDecimal value
|
|
69
72
|
def to_f
|
|
70
73
|
@value.to_f
|
|
71
74
|
end
|
|
72
75
|
|
|
73
76
|
# Creates a new AbcDecimal from a string representation of a number.
|
|
74
|
-
#
|
|
77
|
+
#
|
|
75
78
|
# @param str [String] The string representation of a number to convert to an AbcDecimal
|
|
76
79
|
# @param precision [Integer] The precision to use for the decimal value (number of significant digits after the decimal point). Defaults to DEFAULT_PRECISION
|
|
77
80
|
# @return [AbcDecimal] A new AbcDecimal instance initialized with the given string value and precision
|
|
@@ -80,7 +83,7 @@ module Abachrome
|
|
|
80
83
|
end
|
|
81
84
|
|
|
82
85
|
# Creates a new AbcDecimal from a Rational number.
|
|
83
|
-
#
|
|
86
|
+
#
|
|
84
87
|
# @param rational [Rational] The rational number to convert to an AbcDecimal
|
|
85
88
|
# @param precision [Integer] The precision to use for the decimal representation, defaults to DEFAULT_PRECISION
|
|
86
89
|
# @return [AbcDecimal] A new AbcDecimal instance with the value of the given rational number
|
|
@@ -89,7 +92,7 @@ module Abachrome
|
|
|
89
92
|
end
|
|
90
93
|
|
|
91
94
|
# Creates a new AbcDecimal instance from a float value.
|
|
92
|
-
#
|
|
95
|
+
#
|
|
93
96
|
# @param float [Float] The floating point number to convert to an AbcDecimal
|
|
94
97
|
# @param precision [Integer] The precision to use for the decimal representation (default: DEFAULT_PRECISION)
|
|
95
98
|
# @return [AbcDecimal] A new AbcDecimal instance representing the given float value
|
|
@@ -98,7 +101,7 @@ module Abachrome
|
|
|
98
101
|
end
|
|
99
102
|
|
|
100
103
|
# Creates a new AbcDecimal from an integer value.
|
|
101
|
-
#
|
|
104
|
+
#
|
|
102
105
|
# @param integer [Integer] The integer value to convert to an AbcDecimal
|
|
103
106
|
# @param precision [Integer] The precision to use for the decimal, defaults to DEFAULT_PRECISION
|
|
104
107
|
# @return [AbcDecimal] A new AbcDecimal instance with the specified integer value and precision
|
|
@@ -119,7 +122,7 @@ module Abachrome
|
|
|
119
122
|
end
|
|
120
123
|
|
|
121
124
|
# Subtracts another numeric value from this AbcDecimal.
|
|
122
|
-
#
|
|
125
|
+
#
|
|
123
126
|
# @param other [AbcDecimal, Numeric] The value to subtract from this AbcDecimal.
|
|
124
127
|
# @return [AbcDecimal] A new AbcDecimal representing the result of the subtraction.
|
|
125
128
|
def -(other)
|
|
@@ -128,14 +131,14 @@ module Abachrome
|
|
|
128
131
|
end
|
|
129
132
|
|
|
130
133
|
# Multiplies this AbcDecimal by another value.
|
|
131
|
-
#
|
|
134
|
+
#
|
|
132
135
|
# @param other [Object] The value to multiply by. If not an AbcDecimal, it will be converted to one.
|
|
133
136
|
# @return [AbcDecimal] A new AbcDecimal instance representing the product of this decimal and the other value.
|
|
134
137
|
# @example
|
|
135
138
|
# dec1 = AbcDecimal.new(5)
|
|
136
139
|
# dec2 = AbcDecimal.new(2)
|
|
137
140
|
# result = dec1 * dec2 # => AbcDecimal representing 10
|
|
138
|
-
#
|
|
141
|
+
#
|
|
139
142
|
# # With a non-AbcDecimal value
|
|
140
143
|
# result = dec1 * 3 # => AbcDecimal representing 15
|
|
141
144
|
def *(other)
|
|
@@ -144,7 +147,7 @@ module Abachrome
|
|
|
144
147
|
end
|
|
145
148
|
|
|
146
149
|
# Divides this decimal by another value.
|
|
147
|
-
#
|
|
150
|
+
#
|
|
148
151
|
# @param other [Numeric, AbcDecimal] The divisor, which can be an AbcDecimal instance or any numeric value
|
|
149
152
|
# @return [AbcDecimal] A new AbcDecimal representing the result of the division
|
|
150
153
|
# @example
|
|
@@ -157,7 +160,7 @@ module Abachrome
|
|
|
157
160
|
end
|
|
158
161
|
|
|
159
162
|
# Performs modulo operation with another value.
|
|
160
|
-
#
|
|
163
|
+
#
|
|
161
164
|
# @param other [Numeric, AbcDecimal] The divisor for the modulo operation
|
|
162
165
|
# @return [AbcDecimal] A new AbcDecimal containing the remainder after division
|
|
163
166
|
def %(other)
|
|
@@ -166,7 +169,7 @@ module Abachrome
|
|
|
166
169
|
end
|
|
167
170
|
|
|
168
171
|
# Constrains the value to be between the specified minimum and maximum values.
|
|
169
|
-
#
|
|
172
|
+
#
|
|
170
173
|
# @param min [Numeric, AbcDecimal] The minimum value to clamp to
|
|
171
174
|
# @param max [Numeric, AbcDecimal] The maximum value to clamp to
|
|
172
175
|
# @return [AbcDecimal] A new AbcDecimal within the specified range
|
|
@@ -174,14 +177,14 @@ module Abachrome
|
|
|
174
177
|
# AbcDecimal(5).clamp(0, 10) # => 5
|
|
175
178
|
# AbcDecimal(15).clamp(0, 10) # => 10
|
|
176
179
|
# AbcDecimal(-5).clamp(0, 10) # => 0
|
|
177
|
-
def clamp(min,max)
|
|
178
|
-
@value.clamp(AbcDecimal(min),AbcDecimal(max))
|
|
180
|
+
def clamp(min, max)
|
|
181
|
+
@value.clamp(AbcDecimal(min), AbcDecimal(max))
|
|
179
182
|
end
|
|
180
183
|
|
|
181
184
|
# Raises self to the power of another value.
|
|
182
185
|
# This method handles different input types, including Rational values and
|
|
183
186
|
# other AbcDecimal instances.
|
|
184
|
-
#
|
|
187
|
+
#
|
|
185
188
|
# @param other [Numeric, Rational, AbcDecimal] The exponent to raise this value to
|
|
186
189
|
# @return [AbcDecimal] A new AbcDecimal representing self raised to the power of other
|
|
187
190
|
def **(other)
|
|
@@ -194,7 +197,7 @@ module Abachrome
|
|
|
194
197
|
end
|
|
195
198
|
|
|
196
199
|
# Allows for mixed arithmetic operations between AbcDecimal and other numeric types.
|
|
197
|
-
#
|
|
200
|
+
#
|
|
198
201
|
# @param other [Numeric] The other number to be coerced into an AbcDecimal object
|
|
199
202
|
# @return [Array<AbcDecimal>] A two-element array containing the coerced value and self,
|
|
200
203
|
# allowing Ruby to perform arithmetic operations with mixed types
|
|
@@ -205,7 +208,7 @@ module Abachrome
|
|
|
205
208
|
# Returns a string representation of the decimal value for inspection purposes.
|
|
206
209
|
# This method returns a formatted string that includes the class name and
|
|
207
210
|
# the string representation of the decimal value itself.
|
|
208
|
-
#
|
|
211
|
+
#
|
|
209
212
|
# @return [String] A string in the format "ClassName('value')"
|
|
210
213
|
def inspect
|
|
211
214
|
"#{self.class}('#{self}')"
|
|
@@ -213,7 +216,7 @@ module Abachrome
|
|
|
213
216
|
|
|
214
217
|
# Compares this decimal value with another value for equality.
|
|
215
218
|
# Attempts to convert the other value to an AbcDecimal if it isn't one already.
|
|
216
|
-
#
|
|
219
|
+
#
|
|
217
220
|
# @param other [Object] The value to compare against this AbcDecimal
|
|
218
221
|
# @return [Boolean] True if the values are equal, false otherwise
|
|
219
222
|
def ==(other)
|
|
@@ -222,7 +225,7 @@ module Abachrome
|
|
|
222
225
|
|
|
223
226
|
# Compares this AbcDecimal instance with another AbcDecimal or a value that can be
|
|
224
227
|
# converted to an AbcDecimal.
|
|
225
|
-
#
|
|
228
|
+
#
|
|
226
229
|
# @param other [Object] The value to compare with this AbcDecimal.
|
|
227
230
|
# If not an AbcDecimal, it will be converted using AbcDecimal().
|
|
228
231
|
# @return [Integer, nil] Returns -1 if self is less than other,
|
|
@@ -234,7 +237,7 @@ module Abachrome
|
|
|
234
237
|
end
|
|
235
238
|
|
|
236
239
|
# Compares this decimal with another value.
|
|
237
|
-
#
|
|
240
|
+
#
|
|
238
241
|
# @param other [Object] The value to compare with. Can be an AbcDecimal or any value
|
|
239
242
|
# convertible to AbcDecimal
|
|
240
243
|
# @return [Boolean] true if this decimal is greater than the other value, false otherwise
|
|
@@ -243,7 +246,7 @@ module Abachrome
|
|
|
243
246
|
end
|
|
244
247
|
|
|
245
248
|
# Compares this decimal value with another value.
|
|
246
|
-
#
|
|
249
|
+
#
|
|
247
250
|
# @param other [Object] The value to compare against. If not an AbcDecimal,
|
|
248
251
|
# it will be converted to one.
|
|
249
252
|
# @return [Boolean] true if this decimal is greater than or equal to the other value,
|
|
@@ -253,7 +256,7 @@ module Abachrome
|
|
|
253
256
|
end
|
|
254
257
|
|
|
255
258
|
# Compares this decimal with another value.
|
|
256
|
-
#
|
|
259
|
+
#
|
|
257
260
|
# @param other [Object] The value to compare with. Will be coerced to AbcDecimal if not already an instance.
|
|
258
261
|
# @return [Boolean] true if this decimal is less than the other value, false otherwise.
|
|
259
262
|
# @example
|
|
@@ -266,7 +269,7 @@ module Abachrome
|
|
|
266
269
|
end
|
|
267
270
|
|
|
268
271
|
# Compares this AbcDecimal with another value.
|
|
269
|
-
#
|
|
272
|
+
#
|
|
270
273
|
# @param other [AbcDecimal, Numeric] The value to compare with. If not an AbcDecimal,
|
|
271
274
|
# it will be converted to one.
|
|
272
275
|
# @return [Boolean] true if this AbcDecimal is less than or equal to the other value,
|
|
@@ -277,7 +280,7 @@ module Abachrome
|
|
|
277
280
|
|
|
278
281
|
# @overload round(*args)
|
|
279
282
|
# Rounds this decimal to a specified precision.
|
|
280
|
-
#
|
|
283
|
+
#
|
|
281
284
|
# @param args [Array] Arguments to be passed to BigDecimal#round. Can include
|
|
282
285
|
# the number of decimal places to round to and the rounding mode.
|
|
283
286
|
# @return [AbcDecimal] A new AbcDecimal instance with the rounded value
|
|
@@ -292,9 +295,9 @@ module Abachrome
|
|
|
292
295
|
end
|
|
293
296
|
|
|
294
297
|
# Returns the absolute value (magnitude) of the decimal number.
|
|
295
|
-
#
|
|
298
|
+
#
|
|
296
299
|
# Wraps BigDecimal#abs to ensure return values are properly converted to AbcDecimal.
|
|
297
|
-
#
|
|
300
|
+
#
|
|
298
301
|
# @param args [Array] Optional arguments to pass to BigDecimal#abs
|
|
299
302
|
# @return [AbcDecimal] The absolute value of the decimal number
|
|
300
303
|
def abs(*args)
|
|
@@ -304,23 +307,27 @@ module Abachrome
|
|
|
304
307
|
# Returns the square root of the AbcDecimal value.
|
|
305
308
|
# Calculates the square root by using Ruby's built-in Math.sqrt function
|
|
306
309
|
# and converting the result back to an AbcDecimal.
|
|
307
|
-
#
|
|
310
|
+
#
|
|
308
311
|
# @return [AbcDecimal] A new AbcDecimal representing the square root of the value
|
|
309
312
|
def sqrt
|
|
310
313
|
AbcDecimal(Math.sqrt(@value))
|
|
311
314
|
end
|
|
312
315
|
|
|
313
316
|
# Returns true if the internal value is negative, false otherwise.
|
|
314
|
-
#
|
|
317
|
+
#
|
|
315
318
|
# @return [Boolean] true if the value is negative, false otherwise
|
|
316
319
|
def negative?
|
|
317
320
|
@value.negative?
|
|
318
321
|
end
|
|
319
322
|
|
|
323
|
+
def positive?
|
|
324
|
+
@value > 0
|
|
325
|
+
end
|
|
326
|
+
|
|
320
327
|
# Calculates the arctangent of y/x using the signs of the arguments to determine the quadrant.
|
|
321
328
|
# Unlike the standard Math.atan2, this method accepts AbcDecimal objects or any values
|
|
322
329
|
# that can be converted to AbcDecimal.
|
|
323
|
-
#
|
|
330
|
+
#
|
|
324
331
|
# @param y [AbcDecimal, Numeric] The y coordinate
|
|
325
332
|
# @param x [AbcDecimal, Numeric] The x coordinate
|
|
326
333
|
# @return [AbcDecimal] The angle in radians between the positive x-axis and the ray to the point (x,y)
|
|
@@ -333,10 +340,10 @@ module Abachrome
|
|
|
333
340
|
end
|
|
334
341
|
|
|
335
342
|
# Creates a new AbcDecimal instance.
|
|
336
|
-
#
|
|
343
|
+
#
|
|
337
344
|
# This is a convenience method that allows creating AbcDecimal objects
|
|
338
345
|
# without explicitly referencing the Abachrome namespace.
|
|
339
|
-
#
|
|
346
|
+
#
|
|
340
347
|
# @param args [Array] Arguments to pass to the AbcDecimal constructor
|
|
341
348
|
# @return [Abachrome::AbcDecimal] A new AbcDecimal instance
|
|
342
349
|
def AbcDecimal(*args)
|
|
@@ -344,7 +351,7 @@ def AbcDecimal(*args)
|
|
|
344
351
|
end
|
|
345
352
|
|
|
346
353
|
# Creates a new AbcDecimal instance.
|
|
347
|
-
#
|
|
354
|
+
#
|
|
348
355
|
# @param args [Array] Arguments to pass to AbcDecimal.new
|
|
349
356
|
# @return [Abachrome::AbcDecimal] A new AbcDecimal instance
|
|
350
357
|
# @example
|
|
@@ -354,4 +361,4 @@ def AD(*args)
|
|
|
354
361
|
Abachrome::AbcDecimal.new(*args)
|
|
355
362
|
end
|
|
356
363
|
|
|
357
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
364
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
data/lib/abachrome/color.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::Color - Core color representation class
|
|
2
4
|
#
|
|
3
5
|
# This is the central color class that represents colors across multiple color spaces
|
|
@@ -27,7 +29,7 @@ module Abachrome
|
|
|
27
29
|
attr_reader :color_space, :coordinates, :alpha
|
|
28
30
|
|
|
29
31
|
# Initializes a new Color object with the specified color space, coordinates, and alpha value.
|
|
30
|
-
#
|
|
32
|
+
#
|
|
31
33
|
# @param color_space [ColorSpace] The color space for this color instance
|
|
32
34
|
# @param coordinates [Array<Numeric, String>] The color coordinates in the specified color space
|
|
33
35
|
# @param alpha [Numeric, String] The alpha (opacity) value, between 0.0 and 1.0 (default: 1.0)
|
|
@@ -51,7 +53,7 @@ module Abachrome
|
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
# Creates a new Color instance from RGB values
|
|
54
|
-
#
|
|
56
|
+
#
|
|
55
57
|
# @param r [Numeric] The red component value (typically 0-1)
|
|
56
58
|
# @param g [Numeric] The green component value (typically 0-1)
|
|
57
59
|
# @param b [Numeric] The blue component value (typically 0-1)
|
|
@@ -63,7 +65,7 @@ module Abachrome
|
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
# Creates a new Color instance from LRGB values
|
|
66
|
-
#
|
|
68
|
+
#
|
|
67
69
|
# @param r [Numeric] The red component value (typically 0-1)
|
|
68
70
|
# @param g [Numeric] The green component value (typically 0-1)
|
|
69
71
|
# @param b [Numeric] The blue component value (typically 0-1)
|
|
@@ -75,7 +77,7 @@ module Abachrome
|
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
# Creates a new Color object with OKLAB values.
|
|
78
|
-
#
|
|
80
|
+
#
|
|
79
81
|
# @param l [Float] The lightness component (L) of the OKLAB color space
|
|
80
82
|
# @param a [Float] The green-red component (a) of the OKLAB color space
|
|
81
83
|
# @param b [Float] The blue-yellow component (b) of the OKLAB color space
|
|
@@ -87,7 +89,7 @@ module Abachrome
|
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
# Creates a new color instance in the OKLCH color space.
|
|
90
|
-
#
|
|
92
|
+
#
|
|
91
93
|
# @param l [Numeric] The lightness component (L), typically in range 0..1
|
|
92
94
|
# @param c [Numeric] The chroma component (C), typically starting from 0 with no upper bound
|
|
93
95
|
# @param h [Numeric] The hue component (H) in degrees, typically in range 0..360
|
|
@@ -98,11 +100,60 @@ module Abachrome
|
|
|
98
100
|
new(space, [l, c, h], alpha)
|
|
99
101
|
end
|
|
100
102
|
|
|
103
|
+
# Creates a new Color instance from YIQ values
|
|
104
|
+
#
|
|
105
|
+
# @param y [Numeric] The luma (brightness) component, typically in range 0 to 1
|
|
106
|
+
# @param i [Numeric] The in-phase component (orange-blue), typically in range -0.5957 to 0.5957
|
|
107
|
+
# @param q [Numeric] The quadrature component (purple-green), typically in range -0.5226 to 0.5226
|
|
108
|
+
# @param alpha [Numeric] The alpha (opacity) component value (0-1), defaults to 1.0 (fully opaque)
|
|
109
|
+
# @return [Abachrome::Color] A new Color instance in the YIQ color space
|
|
110
|
+
def self.from_yiq(y, i, q, alpha = 1.0)
|
|
111
|
+
space = ColorSpace.find(:yiq)
|
|
112
|
+
new(space, [y, i, q], alpha)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Creates a new Color instance from CMYK values
|
|
116
|
+
#
|
|
117
|
+
# @param c [Numeric] The cyan component, typically in range 0 to 1
|
|
118
|
+
# @param m [Numeric] The magenta component, typically in range 0 to 1
|
|
119
|
+
# @param y [Numeric] The yellow component, typically in range 0 to 1
|
|
120
|
+
# @param k [Numeric] The key/black component, typically in range 0 to 1
|
|
121
|
+
# @param alpha [Numeric] The alpha (opacity) component value (0-1), defaults to 1.0 (fully opaque)
|
|
122
|
+
# @return [Abachrome::Color] A new Color instance in the CMYK color space
|
|
123
|
+
def self.from_cmyk(c, m, y, k, alpha = 1.0)
|
|
124
|
+
space = ColorSpace.find(:cmyk)
|
|
125
|
+
new(space, [c, m, y, k], alpha)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Creates a new Color instance from XYZ values
|
|
129
|
+
#
|
|
130
|
+
# @param x [Numeric] The X tristimulus value representing the CIE RGB red primary
|
|
131
|
+
# @param y [Numeric] The Y tristimulus value representing luminance
|
|
132
|
+
# @param z [Numeric] The Z tristimulus value representing the CIE RGB blue primary
|
|
133
|
+
# @param alpha [Numeric] The alpha (opacity) component value (0-1), defaults to 1.0 (fully opaque)
|
|
134
|
+
# @return [Abachrome::Color] A new Color instance in the XYZ color space
|
|
135
|
+
def self.from_xyz(x, y, z, alpha = 1.0)
|
|
136
|
+
space = ColorSpace.find(:xyz)
|
|
137
|
+
new(space, [x, y, z], alpha)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Creates a new Color instance from LMS values
|
|
141
|
+
#
|
|
142
|
+
# @param l [Numeric] The long wavelength (L) cone response component
|
|
143
|
+
# @param m [Numeric] The medium wavelength (M) cone response component
|
|
144
|
+
# @param s [Numeric] The short wavelength (S) cone response component
|
|
145
|
+
# @param alpha [Numeric] The alpha (opacity) component value (0-1), defaults to 1.0 (fully opaque)
|
|
146
|
+
# @return [Abachrome::Color] A new Color instance in the LMS color space
|
|
147
|
+
def self.from_lms(l, m, s, alpha = 1.0)
|
|
148
|
+
space = ColorSpace.find(:lms)
|
|
149
|
+
new(space, [l, m, s], alpha)
|
|
150
|
+
end
|
|
151
|
+
|
|
101
152
|
# Compares this color instance with another for equality.
|
|
102
|
-
#
|
|
153
|
+
#
|
|
103
154
|
# Two colors are considered equal if they have the same color space,
|
|
104
155
|
# coordinates, and alpha value.
|
|
105
|
-
#
|
|
156
|
+
#
|
|
106
157
|
# @param other [Object] The object to compare with
|
|
107
158
|
# @return [Boolean] true if the colors are equal, false otherwise
|
|
108
159
|
def ==(other)
|
|
@@ -114,7 +165,7 @@ module Abachrome
|
|
|
114
165
|
end
|
|
115
166
|
|
|
116
167
|
# Checks if this color is equal to another color object.
|
|
117
|
-
#
|
|
168
|
+
#
|
|
118
169
|
# @param other [Object] The object to compare with
|
|
119
170
|
# @return [Boolean] true if the two colors are equal, false otherwise
|
|
120
171
|
# @see ==
|
|
@@ -126,7 +177,7 @@ module Abachrome
|
|
|
126
177
|
# based on its color space, coordinates, and alpha value.
|
|
127
178
|
# The method first converts these components to strings,
|
|
128
179
|
# then computes a hash of the resulting array.
|
|
129
|
-
#
|
|
180
|
+
#
|
|
130
181
|
# @return [Integer] a hash code that can be used for equality comparison
|
|
131
182
|
# and as a hash key in Hash objects
|
|
132
183
|
def hash
|
|
@@ -168,4 +219,4 @@ module Abachrome
|
|
|
168
219
|
end
|
|
169
220
|
end
|
|
170
221
|
|
|
171
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
222
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Abachrome::ColorMixins::Blend - Color blending and mixing functionality
|
|
2
4
|
#
|
|
3
5
|
# This mixin provides methods for blending and mixing colors together in various color spaces.
|
|
@@ -58,7 +60,7 @@ module Abachrome
|
|
|
58
60
|
# Blends this color with another color by the specified amount.
|
|
59
61
|
# This is a destructive version of the blend method, modifying the current
|
|
60
62
|
# color in place.
|
|
61
|
-
#
|
|
63
|
+
#
|
|
62
64
|
# @param other [Abachrome::Color] The color to blend with
|
|
63
65
|
# @param amount [Float] The blend amount, between 0.0 and 1.0, where 0.0 is
|
|
64
66
|
# this color and 1.0 is the other color (default: 0.5)
|
|
@@ -72,7 +74,7 @@ module Abachrome
|
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
# Alias for the blend method that mixes two colors together.
|
|
75
|
-
#
|
|
77
|
+
#
|
|
76
78
|
# @param other [Abachrome::Color] The color to mix with
|
|
77
79
|
# @param amount [Float] The amount to mix, between 0.0 and 1.0, where 0.0 returns the original color and 1.0 returns the other color (default: 0.5)
|
|
78
80
|
# @return [Abachrome::Color] A new color resulting from the mix of the two colors
|
|
@@ -81,10 +83,10 @@ module Abachrome
|
|
|
81
83
|
end
|
|
82
84
|
|
|
83
85
|
# Mix the current color with another color.
|
|
84
|
-
#
|
|
86
|
+
#
|
|
85
87
|
# This method is an alias for blend!. It combines the current color with
|
|
86
88
|
# the provided color at the specified amount.
|
|
87
|
-
#
|
|
89
|
+
#
|
|
88
90
|
# @param other [Abachrome::Color] The color to mix with the current color
|
|
89
91
|
# @param amount [Numeric] The amount of the other color to mix in, from 0 to 1 (default: 0.5)
|
|
90
92
|
# @return [self] Returns the modified color object
|
|
@@ -95,4 +97,4 @@ module Abachrome
|
|
|
95
97
|
end
|
|
96
98
|
end
|
|
97
99
|
|
|
98
|
-
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
100
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Abachrome::ColorMixins::Harmonies - Color Harmony Generation Module
|
|
4
|
+
#
|
|
5
|
+
# This module provides methods for generating color harmonies based on color theory.
|
|
6
|
+
# Color harmonies are sets of colors that work well together according to established
|
|
7
|
+
# design principles. The module uses OKLCH color space for hue manipulation, which
|
|
8
|
+
# provides perceptually uniform results.
|
|
9
|
+
#
|
|
10
|
+
# Key features:
|
|
11
|
+
# - Analogous harmony: Colors adjacent on the color wheel (±30°)
|
|
12
|
+
# - Complementary harmony: Colors opposite on the color wheel (180°)
|
|
13
|
+
# - Triadic harmony: Three colors evenly spaced around the color wheel (120° intervals)
|
|
14
|
+
# - Tetradic/Square harmony: Four colors evenly spaced (90° intervals)
|
|
15
|
+
# - Split-complementary harmony: Base color plus two colors adjacent to its complement (150°, 210°)
|
|
16
|
+
# - Option to generate harmonies in different color spaces (HSL or OKLCH)
|
|
17
|
+
#
|
|
18
|
+
# All harmonies preserve the lightness and chroma of the base color while rotating
|
|
19
|
+
# the hue to create harmonious color combinations.
|
|
20
|
+
#
|
|
21
|
+
# References:
|
|
22
|
+
# - Color Theory: https://en.wikipedia.org/wiki/Color_theory
|
|
23
|
+
# - Color Harmony: https://en.wikipedia.org/wiki/Harmony_(color)
|
|
24
|
+
|
|
25
|
+
module Abachrome
|
|
26
|
+
module ColorMixins
|
|
27
|
+
module Harmonies
|
|
28
|
+
# Generates an analogous color harmony.
|
|
29
|
+
# Analogous colors are adjacent to each other on the color wheel,
|
|
30
|
+
# typically within ±30 degrees. This creates a harmonious, cohesive palette.
|
|
31
|
+
#
|
|
32
|
+
# @param angle [Numeric] The hue angle offset in degrees (default: 30)
|
|
33
|
+
# @param space [Symbol] The color space to use for hue manipulation (:hsl or :oklch, default: :oklch)
|
|
34
|
+
# @return [Array<Abachrome::Color>] An array of three colors: [color at -angle, original, color at +angle]
|
|
35
|
+
def analogous(angle: 30, space: :oklch)
|
|
36
|
+
original_space = color_space
|
|
37
|
+
|
|
38
|
+
# Convert to the specified space for hue manipulation
|
|
39
|
+
color_in_space = space == color_space.id ? self : to_color_space(space)
|
|
40
|
+
l, c, h = color_in_space.coordinates
|
|
41
|
+
|
|
42
|
+
# Generate analogous colors by rotating hue
|
|
43
|
+
color_minus = create_harmony_color(l, c, h - angle, space)
|
|
44
|
+
color_plus = create_harmony_color(l, c, h + angle, space)
|
|
45
|
+
|
|
46
|
+
# Convert back to original color space
|
|
47
|
+
[
|
|
48
|
+
color_minus.to_color_space(original_space.id),
|
|
49
|
+
self,
|
|
50
|
+
color_plus.to_color_space(original_space.id)
|
|
51
|
+
]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Generates a complementary color harmony.
|
|
55
|
+
# Complementary colors are opposite each other on the color wheel (180° apart).
|
|
56
|
+
# They create maximum contrast and vibrant combinations.
|
|
57
|
+
#
|
|
58
|
+
# @param space [Symbol] The color space to use for hue manipulation (:hsl or :oklch, default: :oklch)
|
|
59
|
+
# @return [Array<Abachrome::Color>] An array of two colors: [original, complement]
|
|
60
|
+
def complementary(space: :oklch)
|
|
61
|
+
original_space = color_space
|
|
62
|
+
|
|
63
|
+
# Convert to the specified space for hue manipulation
|
|
64
|
+
color_in_space = space == color_space.id ? self : to_color_space(space)
|
|
65
|
+
l, c, h = color_in_space.coordinates
|
|
66
|
+
|
|
67
|
+
# Generate complementary color by rotating hue 180°
|
|
68
|
+
complement = create_harmony_color(l, c, h + 180, space)
|
|
69
|
+
|
|
70
|
+
# Convert back to original color space
|
|
71
|
+
[
|
|
72
|
+
self,
|
|
73
|
+
complement.to_color_space(original_space.id)
|
|
74
|
+
]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Generates a triadic color harmony.
|
|
78
|
+
# Triadic colors are evenly spaced around the color wheel at 120° intervals.
|
|
79
|
+
# They create vibrant, balanced palettes with good contrast.
|
|
80
|
+
#
|
|
81
|
+
# @param space [Symbol] The color space to use for hue manipulation (:hsl or :oklch, default: :oklch)
|
|
82
|
+
# @return [Array<Abachrome::Color>] An array of three colors evenly spaced around the color wheel
|
|
83
|
+
def triadic(space: :oklch)
|
|
84
|
+
original_space = color_space
|
|
85
|
+
|
|
86
|
+
# Convert to the specified space for hue manipulation
|
|
87
|
+
color_in_space = space == color_space.id ? self : to_color_space(space)
|
|
88
|
+
l, c, h = color_in_space.coordinates
|
|
89
|
+
|
|
90
|
+
# Generate triadic colors by rotating hue 120° and 240°
|
|
91
|
+
color_1 = create_harmony_color(l, c, h + 120, space)
|
|
92
|
+
color_2 = create_harmony_color(l, c, h + 240, space)
|
|
93
|
+
|
|
94
|
+
# Convert back to original color space
|
|
95
|
+
[
|
|
96
|
+
self,
|
|
97
|
+
color_1.to_color_space(original_space.id),
|
|
98
|
+
color_2.to_color_space(original_space.id)
|
|
99
|
+
]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Generates a tetradic (square) color harmony.
|
|
103
|
+
# Tetradic colors are evenly spaced around the color wheel at 90° intervals.
|
|
104
|
+
# They create rich, varied palettes with multiple complementary pairs.
|
|
105
|
+
#
|
|
106
|
+
# @param space [Symbol] The color space to use for hue manipulation (:hsl or :oklch, default: :oklch)
|
|
107
|
+
# @return [Array<Abachrome::Color>] An array of four colors evenly spaced around the color wheel
|
|
108
|
+
def tetradic(space: :oklch)
|
|
109
|
+
original_space = color_space
|
|
110
|
+
|
|
111
|
+
# Convert to the specified space for hue manipulation
|
|
112
|
+
color_in_space = space == color_space.id ? self : to_color_space(space)
|
|
113
|
+
l, c, h = color_in_space.coordinates
|
|
114
|
+
|
|
115
|
+
# Generate tetradic colors by rotating hue 90°, 180°, and 270°
|
|
116
|
+
color_1 = create_harmony_color(l, c, h + 90, space)
|
|
117
|
+
color_2 = create_harmony_color(l, c, h + 180, space)
|
|
118
|
+
color_3 = create_harmony_color(l, c, h + 270, space)
|
|
119
|
+
|
|
120
|
+
# Convert back to original color space
|
|
121
|
+
[
|
|
122
|
+
self,
|
|
123
|
+
color_1.to_color_space(original_space.id),
|
|
124
|
+
color_2.to_color_space(original_space.id),
|
|
125
|
+
color_3.to_color_space(original_space.id)
|
|
126
|
+
]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Generates a split-complementary color harmony.
|
|
130
|
+
# Split-complementary uses the base color plus two colors adjacent to its complement
|
|
131
|
+
# (at 150° and 210° from the base). This creates strong visual contrast while being
|
|
132
|
+
# more subtle than a pure complementary scheme.
|
|
133
|
+
#
|
|
134
|
+
# @param space [Symbol] The color space to use for hue manipulation (:hsl or :oklch, default: :oklch)
|
|
135
|
+
# @return [Array<Abachrome::Color>] An array of three colors: [original, complement-30°, complement+30°]
|
|
136
|
+
def split_complementary(space: :oklch)
|
|
137
|
+
original_space = color_space
|
|
138
|
+
|
|
139
|
+
# Convert to the specified space for hue manipulation
|
|
140
|
+
color_in_space = space == color_space.id ? self : to_color_space(space)
|
|
141
|
+
l, c, h = color_in_space.coordinates
|
|
142
|
+
|
|
143
|
+
# Generate split-complementary colors at 150° and 210° (complement ± 30°)
|
|
144
|
+
color_1 = create_harmony_color(l, c, h + 150, space)
|
|
145
|
+
color_2 = create_harmony_color(l, c, h + 210, space)
|
|
146
|
+
|
|
147
|
+
# Convert back to original color space
|
|
148
|
+
[
|
|
149
|
+
self,
|
|
150
|
+
color_1.to_color_space(original_space.id),
|
|
151
|
+
color_2.to_color_space(original_space.id)
|
|
152
|
+
]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
|
|
157
|
+
# Helper method to create a harmony color with normalized hue
|
|
158
|
+
#
|
|
159
|
+
# @param l [AbcDecimal] The lightness value
|
|
160
|
+
# @param c [AbcDecimal] The chroma value
|
|
161
|
+
# @param h [Numeric] The hue angle in degrees (will be normalized to 0-360)
|
|
162
|
+
# @param space [Symbol] The color space (:hsl or :oklch)
|
|
163
|
+
# @return [Abachrome::Color] A new color in the specified color space
|
|
164
|
+
def create_harmony_color(l, c, h, space)
|
|
165
|
+
# Normalize hue to 0-360 range
|
|
166
|
+
normalized_hue = h % 360
|
|
167
|
+
|
|
168
|
+
case space
|
|
169
|
+
when :oklch
|
|
170
|
+
Abachrome::Color.from_oklch(l, c, normalized_hue, alpha)
|
|
171
|
+
when :hsl
|
|
172
|
+
# For HSL, assuming coordinates are [h, s, l]
|
|
173
|
+
# Note: HSL hue is first coordinate
|
|
174
|
+
Abachrome::Color.new(
|
|
175
|
+
Abachrome::ColorSpace.find(:hsl),
|
|
176
|
+
[normalized_hue, c, l],
|
|
177
|
+
alpha
|
|
178
|
+
)
|
|
179
|
+
else
|
|
180
|
+
raise ArgumentError, "Unsupported color space for harmonies: #{space}. Use :oklch or :hsl"
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Copyright (c) 2025 Durable Programming, LLC. All rights reserved.
|