@planet-matrix/mobius-model 0.5.0 → 0.6.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.
Files changed (175) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +123 -36
  3. package/dist/index.js +45 -4
  4. package/dist/index.js.map +183 -11
  5. package/oxlint.config.ts +6 -0
  6. package/package.json +16 -10
  7. package/src/abort/README.md +92 -0
  8. package/src/abort/abort-manager.ts +278 -0
  9. package/src/abort/abort-signal-listener-manager.ts +81 -0
  10. package/src/abort/index.ts +2 -0
  11. package/src/basic/README.md +69 -118
  12. package/src/basic/function.ts +81 -62
  13. package/src/basic/is.ts +152 -71
  14. package/src/basic/promise.ts +29 -8
  15. package/src/basic/string.ts +2 -33
  16. package/src/color/README.md +105 -0
  17. package/src/color/index.ts +3 -0
  18. package/src/color/internal.ts +42 -0
  19. package/src/color/rgb/analyze.ts +236 -0
  20. package/src/color/rgb/construct.ts +130 -0
  21. package/src/color/rgb/convert.ts +227 -0
  22. package/src/color/rgb/derive.ts +303 -0
  23. package/src/color/rgb/index.ts +6 -0
  24. package/src/color/rgb/internal.ts +208 -0
  25. package/src/color/rgb/parse.ts +302 -0
  26. package/src/color/rgb/serialize.ts +144 -0
  27. package/src/color/types.ts +57 -0
  28. package/src/color/xyz/analyze.ts +80 -0
  29. package/src/color/xyz/construct.ts +19 -0
  30. package/src/color/xyz/convert.ts +71 -0
  31. package/src/color/xyz/index.ts +3 -0
  32. package/src/color/xyz/internal.ts +23 -0
  33. package/src/css/README.md +93 -0
  34. package/src/css/class.ts +559 -0
  35. package/src/css/index.ts +1 -0
  36. package/src/encoding/README.md +66 -79
  37. package/src/encoding/base64.ts +13 -4
  38. package/src/environment/README.md +97 -0
  39. package/src/environment/basic.ts +26 -0
  40. package/src/environment/device.ts +311 -0
  41. package/src/environment/feature.ts +285 -0
  42. package/src/environment/geo.ts +337 -0
  43. package/src/environment/index.ts +7 -0
  44. package/src/environment/runtime.ts +400 -0
  45. package/src/environment/snapshot.ts +60 -0
  46. package/src/environment/variable.ts +239 -0
  47. package/src/event/README.md +90 -0
  48. package/src/event/class-event-proxy.ts +228 -0
  49. package/src/event/common.ts +19 -0
  50. package/src/event/event-manager.ts +203 -0
  51. package/src/event/index.ts +4 -0
  52. package/src/event/instance-event-proxy.ts +186 -0
  53. package/src/event/internal.ts +24 -0
  54. package/src/exception/README.md +96 -0
  55. package/src/exception/browser.ts +219 -0
  56. package/src/exception/index.ts +4 -0
  57. package/src/exception/nodejs.ts +169 -0
  58. package/src/exception/normalize.ts +106 -0
  59. package/src/exception/types.ts +99 -0
  60. package/src/identifier/README.md +92 -0
  61. package/src/identifier/id.ts +119 -0
  62. package/src/identifier/index.ts +2 -0
  63. package/src/identifier/uuid.ts +187 -0
  64. package/src/index.ts +16 -1
  65. package/src/log/README.md +79 -0
  66. package/src/log/index.ts +5 -0
  67. package/src/log/log-emitter.ts +72 -0
  68. package/src/log/log-record.ts +10 -0
  69. package/src/log/log-scheduler.ts +74 -0
  70. package/src/log/log-type.ts +8 -0
  71. package/src/log/logger.ts +543 -0
  72. package/src/orchestration/README.md +89 -0
  73. package/src/orchestration/coordination/barrier.ts +214 -0
  74. package/src/orchestration/coordination/count-down-latch.ts +215 -0
  75. package/src/orchestration/coordination/errors.ts +98 -0
  76. package/src/orchestration/coordination/index.ts +16 -0
  77. package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
  78. package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
  79. package/src/orchestration/coordination/keyed-lock.ts +168 -0
  80. package/src/orchestration/coordination/mutex.ts +257 -0
  81. package/src/orchestration/coordination/permit.ts +127 -0
  82. package/src/orchestration/coordination/read-write-lock.ts +444 -0
  83. package/src/orchestration/coordination/semaphore.ts +280 -0
  84. package/src/orchestration/index.ts +1 -0
  85. package/src/random/README.md +55 -86
  86. package/src/random/index.ts +1 -1
  87. package/src/random/string.ts +35 -0
  88. package/src/reactor/README.md +4 -0
  89. package/src/reactor/reactor-core/primitive.ts +9 -9
  90. package/src/reactor/reactor-core/reactive-system.ts +5 -5
  91. package/src/singleton/README.md +79 -0
  92. package/src/singleton/factory.ts +55 -0
  93. package/src/singleton/index.ts +2 -0
  94. package/src/singleton/manager.ts +204 -0
  95. package/src/storage/README.md +107 -0
  96. package/src/storage/index.ts +1 -0
  97. package/src/storage/table.ts +449 -0
  98. package/src/timer/README.md +86 -0
  99. package/src/timer/expiration/expiration-manager.ts +594 -0
  100. package/src/timer/expiration/index.ts +3 -0
  101. package/src/timer/expiration/min-heap.ts +208 -0
  102. package/src/timer/expiration/remaining-manager.ts +241 -0
  103. package/src/timer/index.ts +1 -0
  104. package/src/type/README.md +54 -307
  105. package/src/type/class.ts +2 -2
  106. package/src/type/index.ts +14 -14
  107. package/src/type/is.ts +265 -2
  108. package/src/type/object.ts +37 -0
  109. package/src/type/string.ts +7 -2
  110. package/src/type/tuple.ts +6 -6
  111. package/src/type/union.ts +16 -0
  112. package/src/web/README.md +77 -0
  113. package/src/web/capture.ts +35 -0
  114. package/src/web/clipboard.ts +97 -0
  115. package/src/web/dom.ts +117 -0
  116. package/src/web/download.ts +16 -0
  117. package/src/web/event.ts +46 -0
  118. package/src/web/index.ts +10 -0
  119. package/src/web/local-storage.ts +113 -0
  120. package/src/web/location.ts +28 -0
  121. package/src/web/permission.ts +172 -0
  122. package/src/web/script-loader.ts +432 -0
  123. package/tests/unit/abort/abort-manager.spec.ts +225 -0
  124. package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
  125. package/tests/unit/basic/array.spec.ts +1 -1
  126. package/tests/unit/basic/stream.spec.ts +1 -1
  127. package/tests/unit/basic/string.spec.ts +0 -9
  128. package/tests/unit/color/rgb/analyze.spec.ts +110 -0
  129. package/tests/unit/color/rgb/construct.spec.ts +56 -0
  130. package/tests/unit/color/rgb/convert.spec.ts +60 -0
  131. package/tests/unit/color/rgb/derive.spec.ts +103 -0
  132. package/tests/unit/color/rgb/parse.spec.ts +66 -0
  133. package/tests/unit/color/rgb/serialize.spec.ts +46 -0
  134. package/tests/unit/color/xyz/analyze.spec.ts +33 -0
  135. package/tests/unit/color/xyz/construct.spec.ts +10 -0
  136. package/tests/unit/color/xyz/convert.spec.ts +18 -0
  137. package/tests/unit/css/class.spec.ts +157 -0
  138. package/tests/unit/environment/basic.spec.ts +20 -0
  139. package/tests/unit/environment/device.spec.ts +146 -0
  140. package/tests/unit/environment/feature.spec.ts +388 -0
  141. package/tests/unit/environment/geo.spec.ts +111 -0
  142. package/tests/unit/environment/runtime.spec.ts +364 -0
  143. package/tests/unit/environment/snapshot.spec.ts +4 -0
  144. package/tests/unit/environment/variable.spec.ts +190 -0
  145. package/tests/unit/event/class-event-proxy.spec.ts +225 -0
  146. package/tests/unit/event/event-manager.spec.ts +246 -0
  147. package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
  148. package/tests/unit/exception/browser.spec.ts +213 -0
  149. package/tests/unit/exception/nodejs.spec.ts +144 -0
  150. package/tests/unit/exception/normalize.spec.ts +57 -0
  151. package/tests/unit/identifier/id.spec.ts +71 -0
  152. package/tests/unit/identifier/uuid.spec.ts +85 -0
  153. package/tests/unit/log/log-emitter.spec.ts +33 -0
  154. package/tests/unit/log/log-scheduler.spec.ts +40 -0
  155. package/tests/unit/log/log-type.spec.ts +7 -0
  156. package/tests/unit/log/logger.spec.ts +222 -0
  157. package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
  158. package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
  159. package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
  160. package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
  161. package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
  162. package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
  163. package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
  164. package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
  165. package/tests/unit/random/string.spec.ts +11 -0
  166. package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
  167. package/tests/unit/reactor/preact-signal.spec.ts +1 -2
  168. package/tests/unit/singleton/singleton.spec.ts +49 -0
  169. package/tests/unit/storage/table.spec.ts +620 -0
  170. package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
  171. package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
  172. package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
  173. package/.oxlintrc.json +0 -5
  174. package/src/random/uuid.ts +0 -103
  175. package/tests/unit/random/uuid.spec.ts +0 -37
@@ -0,0 +1,3 @@
1
+ export type * from "./types.ts"
2
+ export * from "./xyz/index.ts"
3
+ export * from "./rgb/index.ts"
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 断言一个数值是有限数字。
3
+ */
4
+ export const internalAssertFinite = (value: number, name: string): void => {
5
+ if (Number.isFinite(value) === false) {
6
+ throw new RangeError(`${name} must be a finite number`)
7
+ }
8
+ }
9
+
10
+ /**
11
+ * 断言一个数值是非负有限数字。
12
+ */
13
+ export const internalAssertNonNegativeFinite = (value: number, name: string): void => {
14
+ internalAssertFinite(value, name)
15
+
16
+ if (value < 0) {
17
+ throw new RangeError(`${name} must be a non-negative finite number`)
18
+ }
19
+ }
20
+
21
+ /**
22
+ * 断言一个颜色单位值位于 `0` 到 `1` 之间。
23
+ */
24
+ export const internalAssertColorUnit = (value: number, unitName: string): void => {
25
+ if (Number.isFinite(value) === false || value < 0 || value > 1) {
26
+ throw new RangeError(`Color ${unitName} must be a finite number between 0 and 1`)
27
+ }
28
+ }
29
+
30
+ /**
31
+ * 断言一个透明度值位于 `0` 到 `1` 之间。
32
+ */
33
+ export const internalAssertAlpha = (value: number): void => {
34
+ internalAssertColorUnit(value, "alpha")
35
+ }
36
+
37
+ /**
38
+ * 将浮点数按颜色计算常用精度进行圆整。
39
+ */
40
+ export const internalRoundColorFloat = (value: number): number => {
41
+ return Number(value.toFixed(6))
42
+ }
@@ -0,0 +1,236 @@
1
+ import { createSrgbColorValue } from "./construct.ts"
2
+ import { internalAssertColorUnit } from "../internal.ts"
3
+ import { linearRgbColorValueToXyzColorValue } from "../xyz/convert.ts"
4
+ import { srgbColorValueToLinearRgbColorValue } from "./convert.ts"
5
+ import {
6
+ xyzColorValueToRelativeLuminance,
7
+ xyzColorValueToTristimulusSum,
8
+ xyzColorValueToXyChromaticityValue,
9
+ } from "../xyz/analyze.ts"
10
+
11
+ import type {
12
+ LinearRgbColorValue,
13
+ SrgbColorValue,
14
+ XyChromaticityValue,
15
+ } from "../types.ts"
16
+
17
+ const internalDefaultDarkSrgbColorValue: SrgbColorValue = { red: 0, green: 0, blue: 0, alpha: 1 }
18
+ const internalDefaultLightSrgbColorValue: SrgbColorValue = { red: 255, green: 255, blue: 255, alpha: 1 }
19
+
20
+ const internalNormalizeContrastRatio = (value: number): number => {
21
+ if (Number.isFinite(value) === false || value < 1) {
22
+ throw new RangeError("Contrast ratio must be a finite number greater than or equal to 1")
23
+ }
24
+
25
+ return value
26
+ }
27
+
28
+ /**
29
+ * 读取 Linear RGB 颜色值的相对亮度。
30
+ *
31
+ * 该函数会沿合法路径 `Linear RGB -> XYZ -> luminance` 计算。
32
+ *
33
+ * @example
34
+ * ```
35
+ * // Expect: 0.212673
36
+ * const example1 = linearRgbColorValueToRelativeLuminance({ red: 1, green: 0, blue: 0, alpha: 1 })
37
+ *
38
+ * // Expect: 1
39
+ * const example2 = linearRgbColorValueToRelativeLuminance({ red: 1, green: 1, blue: 1, alpha: 1 })
40
+ * ```
41
+ */
42
+ export const linearRgbColorValueToRelativeLuminance = (color: LinearRgbColorValue): number => {
43
+ return xyzColorValueToRelativeLuminance(linearRgbColorValueToXyzColorValue(color))
44
+ }
45
+
46
+ /**
47
+ * 读取 sRGB 颜色值的相对亮度。
48
+ *
49
+ * 该函数会沿合法路径 `sRGB -> Linear RGB -> XYZ -> luminance` 计算。
50
+ *
51
+ * @example
52
+ * ```
53
+ * // Expect: 0.125053
54
+ * const example1 = srgbColorValueToRelativeLuminance({ red: 51, green: 102, blue: 153, alpha: 1 })
55
+ *
56
+ * // Expect: 1
57
+ * const example2 = srgbColorValueToRelativeLuminance({ red: 255, green: 255, blue: 255, alpha: 1 })
58
+ * ```
59
+ */
60
+ export const srgbColorValueToRelativeLuminance = (color: SrgbColorValue): number => {
61
+ return linearRgbColorValueToRelativeLuminance(srgbColorValueToLinearRgbColorValue(color))
62
+ }
63
+
64
+ /**
65
+ * 读取两个 sRGB 颜色值之间的对比度。
66
+ *
67
+ * 该函数假定输入已经是最终显示颜色;若存在透明叠加,应先完成合成。
68
+ *
69
+ * @example
70
+ * ```
71
+ * // Expect: 21
72
+ * const example1 = srgbColorValuesToContrastRatio(
73
+ * { red: 0, green: 0, blue: 0, alpha: 1 },
74
+ * { red: 255, green: 255, blue: 255, alpha: 1 },
75
+ * )
76
+ *
77
+ * // Expect: 1
78
+ * const example2 = srgbColorValuesToContrastRatio(
79
+ * { red: 51, green: 102, blue: 153, alpha: 1 },
80
+ * { red: 51, green: 102, blue: 153, alpha: 1 },
81
+ * )
82
+ * ```
83
+ */
84
+ export const srgbColorValuesToContrastRatio = (first: SrgbColorValue, second: SrgbColorValue): number => {
85
+ const firstLuminance = srgbColorValueToRelativeLuminance(first)
86
+ const secondLuminance = srgbColorValueToRelativeLuminance(second)
87
+ const lighterLuminance = Math.max(firstLuminance, secondLuminance)
88
+ const darkerLuminance = Math.min(firstLuminance, secondLuminance)
89
+
90
+ return lighterLuminance === darkerLuminance
91
+ ? 1
92
+ : Number((((lighterLuminance + 0.05) / (darkerLuminance + 0.05))).toFixed(6))
93
+ }
94
+
95
+ /**
96
+ * 判断两个 sRGB 颜色值是否达到指定对比度阈值。
97
+ *
98
+ * 该函数假定输入已经是最终显示颜色;若存在透明叠加,应先完成合成。
99
+ *
100
+ * @example
101
+ * ```
102
+ * // Expect: true
103
+ * const example1 = doSrgbColorValuesMeetContrastRatio(
104
+ * { red: 0, green: 0, blue: 0, alpha: 1 },
105
+ * { red: 255, green: 255, blue: 255, alpha: 1 },
106
+ * 4.5,
107
+ * )
108
+ *
109
+ * // Expect: false
110
+ * const example2 = doSrgbColorValuesMeetContrastRatio(
111
+ * { red: 120, green: 120, blue: 120, alpha: 1 },
112
+ * { red: 255, green: 255, blue: 255, alpha: 1 },
113
+ * 4.5,
114
+ * )
115
+ * ```
116
+ */
117
+ export const doSrgbColorValuesMeetContrastRatio = (first: SrgbColorValue, second: SrgbColorValue, minimumContrastRatio = 4.5): boolean => {
118
+ return srgbColorValuesToContrastRatio(first, second) >= internalNormalizeContrastRatio(minimumContrastRatio)
119
+ }
120
+
121
+ /**
122
+ * 为给定背景色选择更可读的黑色或白色文本颜色。
123
+ *
124
+ * 该函数假定背景色已经是最终显示颜色。
125
+ *
126
+ * @example
127
+ * ```
128
+ * // Expect: { red: 255, green: 255, blue: 255, alpha: 1 }
129
+ * const example1 = pickReadableSrgbTextColorValue({ red: 0, green: 102, blue: 204, alpha: 1 })
130
+ *
131
+ * // Expect: { red: 0, green: 0, blue: 0, alpha: 1 }
132
+ * const example2 = pickReadableSrgbTextColorValue({ red: 250, green: 240, blue: 210, alpha: 1 })
133
+ * ```
134
+ */
135
+ export const pickReadableSrgbTextColorValue = (
136
+ backgroundColor: SrgbColorValue,
137
+ options: { darkColor?: SrgbColorValue, lightColor?: SrgbColorValue } = {},
138
+ ): SrgbColorValue => {
139
+ const normalizedDarkColor = createSrgbColorValue(options.darkColor ?? internalDefaultDarkSrgbColorValue)
140
+ const normalizedLightColor = createSrgbColorValue(options.lightColor ?? internalDefaultLightSrgbColorValue)
141
+ const darkContrastRatio = srgbColorValuesToContrastRatio(normalizedDarkColor, backgroundColor)
142
+ const lightContrastRatio = srgbColorValuesToContrastRatio(normalizedLightColor, backgroundColor)
143
+
144
+ return lightContrastRatio > darkContrastRatio ? normalizedLightColor : normalizedDarkColor
145
+ }
146
+
147
+ /**
148
+ * 读取 Linear RGB 颜色值沿合法路径得到的三刺激总量。
149
+ *
150
+ * 该函数会沿合法路径 `Linear RGB -> XYZ -> tristimulus sum` 计算。
151
+ *
152
+ * @example
153
+ * ```
154
+ * // Expect: 0.644463
155
+ * const example1 = linearRgbColorValueToTristimulusSum({ red: 1, green: 0, blue: 0, alpha: 1 })
156
+ *
157
+ * // Expect: 3.0393
158
+ * const example2 = linearRgbColorValueToTristimulusSum({ red: 1, green: 1, blue: 1, alpha: 1 })
159
+ * ```
160
+ */
161
+ export const linearRgbColorValueToTristimulusSum = (color: LinearRgbColorValue): number => {
162
+ return xyzColorValueToTristimulusSum(linearRgbColorValueToXyzColorValue(color))
163
+ }
164
+
165
+ /**
166
+ * 读取 sRGB 颜色值沿合法路径得到的三刺激总量。
167
+ *
168
+ * 该函数会沿合法路径 `sRGB -> Linear RGB -> XYZ -> tristimulus sum` 计算。
169
+ *
170
+ * @example
171
+ * ```
172
+ * // Expect: 0.562889
173
+ * const example1 = srgbColorValueToTristimulusSum({ red: 51, green: 102, blue: 153, alpha: 1 })
174
+ *
175
+ * // Expect: 3.0393
176
+ * const example2 = srgbColorValueToTristimulusSum({ red: 255, green: 255, blue: 255, alpha: 1 })
177
+ * ```
178
+ */
179
+ export const srgbColorValueToTristimulusSum = (color: SrgbColorValue): number => {
180
+ return linearRgbColorValueToTristimulusSum(srgbColorValueToLinearRgbColorValue(color))
181
+ }
182
+
183
+ /**
184
+ * 读取 Linear RGB 颜色值沿合法路径得到的 `xy` 色度坐标。
185
+ *
186
+ * 该函数会沿合法路径 `Linear RGB -> XYZ -> xy chromaticity` 计算。
187
+ *
188
+ * @example
189
+ * ```
190
+ * // Expect: { x: 0.64, y: 0.33 }
191
+ * const example1 = linearRgbColorValueToXyChromaticityValue({ red: 1, green: 0, blue: 0, alpha: 1 })
192
+ *
193
+ * // Expect: { x: 0.312727, y: 0.329023 }
194
+ * const example2 = linearRgbColorValueToXyChromaticityValue({ red: 1, green: 1, blue: 1, alpha: 1 })
195
+ * ```
196
+ */
197
+ export const linearRgbColorValueToXyChromaticityValue = (color: LinearRgbColorValue): XyChromaticityValue => {
198
+ return xyzColorValueToXyChromaticityValue(linearRgbColorValueToXyzColorValue(color))
199
+ }
200
+
201
+ /**
202
+ * 读取 sRGB 颜色值沿合法路径得到的 `xy` 色度坐标。
203
+ *
204
+ * 该函数会沿合法路径 `sRGB -> Linear RGB -> XYZ -> xy chromaticity` 计算。
205
+ *
206
+ * @example
207
+ * ```
208
+ * // Expect: { x: 0.210775, y: 0.222163 }
209
+ * const example1 = srgbColorValueToXyChromaticityValue({ red: 51, green: 102, blue: 153, alpha: 1 })
210
+ *
211
+ * // Expect: { x: 0.312727, y: 0.329023 }
212
+ * const example2 = srgbColorValueToXyChromaticityValue({ red: 255, green: 255, blue: 255, alpha: 1 })
213
+ * ```
214
+ */
215
+ export const srgbColorValueToXyChromaticityValue = (color: SrgbColorValue): XyChromaticityValue => {
216
+ return linearRgbColorValueToXyChromaticityValue(srgbColorValueToLinearRgbColorValue(color))
217
+ }
218
+
219
+ /**
220
+ * 判断一个 sRGB 颜色值是否可视为亮色。
221
+ *
222
+ * 阈值位于 `0` 到 `1` 之间,判断依据是相对亮度。
223
+ *
224
+ * @example
225
+ * ```
226
+ * // Expect: true
227
+ * const example1 = isLightSrgbColorValue({ red: 255, green: 255, blue: 255, alpha: 1 })
228
+ *
229
+ * // Expect: false
230
+ * const example2 = isLightSrgbColorValue({ red: 0, green: 0, blue: 0, alpha: 1 })
231
+ * ```
232
+ */
233
+ export const isLightSrgbColorValue = (color: SrgbColorValue, threshold = 0.5): boolean => {
234
+ internalAssertColorUnit(threshold, "threshold")
235
+ return srgbColorValueToRelativeLuminance(createSrgbColorValue(color)) >= threshold
236
+ }
@@ -0,0 +1,130 @@
1
+ import {
2
+ internalGetStableSrgbColorValueByString,
3
+ internalNormalizeHslColorValue,
4
+ internalNormalizeHsvColorValue,
5
+ internalNormalizeLinearRgbColorValue,
6
+ internalNormalizeSrgbColorValue,
7
+ } from "./internal.ts"
8
+ import {
9
+ srgbColorValueToHslColorValue,
10
+ srgbColorValueToHsvColorValue,
11
+ } from "./convert.ts"
12
+
13
+ import type {
14
+ HslColorValue,
15
+ HsvColorValue,
16
+ LinearRgbColorValue,
17
+ SrgbColorValue,
18
+ } from "../types.ts"
19
+
20
+ /**
21
+ * 构造一个 Linear RGB 颜色值对象。
22
+ *
23
+ * @example
24
+ * ```
25
+ * // Expect: { red: 0.2, green: 0.4, blue: 0.6, alpha: 1 }
26
+ * const example1 = createLinearRgbColorValue({ red: 0.2, green: 0.4, blue: 0.6, alpha: 1 })
27
+ *
28
+ * // Expect: { red: -0.25, green: 0.4, blue: 1.2, alpha: 0.5 }
29
+ * const example2 = createLinearRgbColorValue({ red: -0.25, green: 0.4, blue: 1.2, alpha: 0.5 })
30
+ * ```
31
+ */
32
+ export const createLinearRgbColorValue = (color: LinearRgbColorValue): LinearRgbColorValue => {
33
+ return internalNormalizeLinearRgbColorValue(color)
34
+ }
35
+
36
+ /**
37
+ * 构造一个 sRGB 颜色值对象。
38
+ *
39
+ * @example
40
+ * ```
41
+ * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 }
42
+ * const example1 = createSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 })
43
+ *
44
+ * // Expect: { red: 255, green: 255, blue: 255, alpha: 0.5 }
45
+ * const example2 = createSrgbColorValue({ red: 255, green: 255, blue: 255, alpha: 0.5 })
46
+ * ```
47
+ */
48
+ export const createSrgbColorValue = (color: SrgbColorValue): SrgbColorValue => {
49
+ return internalNormalizeSrgbColorValue(color)
50
+ }
51
+
52
+ /**
53
+ * 构造一个 HSL 颜色值对象。
54
+ *
55
+ * @example
56
+ * ```
57
+ * // Expect: { hue: 330, saturation: 0.5, lightness: 0.4, alpha: 1 }
58
+ * const example1 = createHslColorValue({ hue: -30, saturation: 0.5, lightness: 0.4, alpha: 1 })
59
+ *
60
+ * // Expect: { hue: 0, saturation: 0, lightness: 1, alpha: 0.5 }
61
+ * const example2 = createHslColorValue({ hue: 360, saturation: 0, lightness: 1, alpha: 0.5 })
62
+ * ```
63
+ */
64
+ export const createHslColorValue = (color: HslColorValue): HslColorValue => {
65
+ return internalNormalizeHslColorValue(color)
66
+ }
67
+
68
+ /**
69
+ * 构造一个 HSV 颜色值对象。
70
+ *
71
+ * @example
72
+ * ```
73
+ * // Expect: { hue: 330, saturation: 0.5, value: 0.6, alpha: 1 }
74
+ * const example1 = createHsvColorValue({ hue: -30, saturation: 0.5, value: 0.6, alpha: 1 })
75
+ *
76
+ * // Expect: { hue: 30, saturation: 0.5, value: 0.6, alpha: 1 }
77
+ * const example2 = createHsvColorValue({ hue: 390, saturation: 0.5, value: 0.6, alpha: 1 })
78
+ * ```
79
+ */
80
+ export const createHsvColorValue = (color: HsvColorValue): HsvColorValue => {
81
+ return internalNormalizeHsvColorValue(color)
82
+ }
83
+
84
+ /**
85
+ * 根据稳定字符串构造 sRGB 颜色值。
86
+ *
87
+ * @example
88
+ * ```
89
+ * // Expect: { red: 254, green: 192, blue: 46, alpha: 1 }
90
+ * const example1 = createSrgbColorValueByString("mobius")
91
+ *
92
+ * // Expect: { red: 101, green: 227, blue: 144, alpha: 1 }
93
+ * const example2 = createSrgbColorValueByString("planet")
94
+ * ```
95
+ */
96
+ export const createSrgbColorValueByString = (input: string): SrgbColorValue => {
97
+ return internalGetStableSrgbColorValueByString(input)
98
+ }
99
+
100
+ /**
101
+ * 根据稳定字符串构造 HSL 颜色值。
102
+ *
103
+ * @example
104
+ * ```
105
+ * // Expect: { hue: 42.992126, saturation: 0.991597, lightness: 0.588235, alpha: 1 }
106
+ * const example1 = createHslColorValueByString("mobius")
107
+ *
108
+ * // Expect: { hue: 139.777778, saturation: 0.71831, lightness: 0.643137, alpha: 1 }
109
+ * const example2 = createHslColorValueByString("planet")
110
+ * ```
111
+ */
112
+ export const createHslColorValueByString = (input: string): HslColorValue => {
113
+ return srgbColorValueToHslColorValue(createSrgbColorValueByString(input))
114
+ }
115
+
116
+ /**
117
+ * 根据稳定字符串构造 HSV 颜色值。
118
+ *
119
+ * @example
120
+ * ```
121
+ * // Expect: { hue: 42.992126, saturation: 0.818898, value: 0.996078, alpha: 1 }
122
+ * const example1 = createHsvColorValueByString("mobius")
123
+ *
124
+ * // Expect: { hue: 139.777778, saturation: 0.555066, value: 0.890196, alpha: 1 }
125
+ * const example2 = createHsvColorValueByString("planet")
126
+ * ```
127
+ */
128
+ export const createHsvColorValueByString = (input: string): HsvColorValue => {
129
+ return srgbColorValueToHsvColorValue(createSrgbColorValueByString(input))
130
+ }
@@ -0,0 +1,227 @@
1
+ import {
2
+ internalRoundColorFloat,
3
+ } from "../internal.ts"
4
+ import {
5
+ internalGetLinearRgbChannelBySrgbChannel,
6
+ internalGetSrgbChannelByLinearRgbChannel,
7
+ internalNormalizeHslColorValue,
8
+ internalNormalizeHsvColorValue,
9
+ internalNormalizeLinearRgbColorValue,
10
+ internalNormalizeSrgbColorValue,
11
+ } from "./internal.ts"
12
+
13
+ import type {
14
+ HslColorValue,
15
+ HsvColorValue,
16
+ LinearRgbColorValue,
17
+ SrgbColorValue,
18
+ } from "../types.ts"
19
+
20
+ const internalGetHueByNormalizedRgbChannels = (red: number, green: number, blue: number, delta: number, max: number): number => {
21
+ if (delta === 0) {
22
+ return 0
23
+ }
24
+
25
+ let hue = 0
26
+
27
+ if (max === red) {
28
+ hue = 60 * (((green - blue) / delta) % 6)
29
+ } else if (max === green) {
30
+ hue = 60 * (((blue - red) / delta) + 2)
31
+ } else {
32
+ hue = 60 * (((red - green) / delta) + 4)
33
+ }
34
+
35
+ if (hue < 0) {
36
+ return hue + 360
37
+ }
38
+
39
+ return hue
40
+ }
41
+ const internalGetNormalizedRgbChannelsByHue = (hue: number, chroma: number): { red: number, green: number, blue: number } => {
42
+ const segment = hue / 60
43
+ const secondary = chroma * (1 - Math.abs((segment % 2) - 1))
44
+
45
+ if (segment >= 0 && segment < 1) {
46
+ return { red: chroma, green: secondary, blue: 0 }
47
+ }
48
+
49
+ if (segment >= 1 && segment < 2) {
50
+ return { red: secondary, green: chroma, blue: 0 }
51
+ }
52
+
53
+ if (segment >= 2 && segment < 3) {
54
+ return { red: 0, green: chroma, blue: secondary }
55
+ }
56
+
57
+ if (segment >= 3 && segment < 4) {
58
+ return { red: 0, green: secondary, blue: chroma }
59
+ }
60
+
61
+ if (segment >= 4 && segment < 5) {
62
+ return { red: secondary, green: 0, blue: chroma }
63
+ }
64
+
65
+ return { red: chroma, green: 0, blue: secondary }
66
+ }
67
+
68
+ /**
69
+ * 将 Linear RGB 颜色值转换为 sRGB 颜色值。
70
+ *
71
+ * @example
72
+ * ```
73
+ * // Expect: { red: 255, green: 0, blue: 0, alpha: 1 }
74
+ * const example1 = linearRgbColorValueToSrgbColorValue({ red: 1, green: 0, blue: 0, alpha: 1 })
75
+ *
76
+ * // Expect: { red: 188, green: 188, blue: 188, alpha: 0.5 }
77
+ * const example2 = linearRgbColorValueToSrgbColorValue({ red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5 })
78
+ * ```
79
+ */
80
+ export const linearRgbColorValueToSrgbColorValue = (color: LinearRgbColorValue): SrgbColorValue => {
81
+ const normalizedColor = internalNormalizeLinearRgbColorValue(color)
82
+
83
+ return internalNormalizeSrgbColorValue({
84
+ red: internalGetSrgbChannelByLinearRgbChannel(normalizedColor.red, "red"),
85
+ green: internalGetSrgbChannelByLinearRgbChannel(normalizedColor.green, "green"),
86
+ blue: internalGetSrgbChannelByLinearRgbChannel(normalizedColor.blue, "blue"),
87
+ alpha: normalizedColor.alpha,
88
+ })
89
+ }
90
+
91
+ /**
92
+ * 将 sRGB 颜色值转换为 Linear RGB 颜色值。
93
+ *
94
+ * @example
95
+ * ```
96
+ * // Expect: { red: 1, green: 0, blue: 0, alpha: 1 }
97
+ * const example1 = srgbColorValueToLinearRgbColorValue({ red: 255, green: 0, blue: 0, alpha: 1 })
98
+ *
99
+ * // Expect: { red: 0.215861, green: 0.215861, blue: 0.215861, alpha: 0.5 }
100
+ * const example2 = srgbColorValueToLinearRgbColorValue({ red: 128, green: 128, blue: 128, alpha: 0.5 })
101
+ * ```
102
+ */
103
+ export const srgbColorValueToLinearRgbColorValue = (color: SrgbColorValue): LinearRgbColorValue => {
104
+ const normalizedColor = internalNormalizeSrgbColorValue(color)
105
+
106
+ return internalNormalizeLinearRgbColorValue({
107
+ red: internalGetLinearRgbChannelBySrgbChannel(normalizedColor.red),
108
+ green: internalGetLinearRgbChannelBySrgbChannel(normalizedColor.green),
109
+ blue: internalGetLinearRgbChannelBySrgbChannel(normalizedColor.blue),
110
+ alpha: normalizedColor.alpha,
111
+ })
112
+ }
113
+
114
+ /**
115
+ * 将 sRGB 颜色值转换为 HSL 颜色值。
116
+ *
117
+ * @example
118
+ * ```
119
+ * // Expect: { hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 }
120
+ * const example1 = srgbColorValueToHslColorValue({ red: 51, green: 102, blue: 153, alpha: 1 })
121
+ *
122
+ * // Expect: { hue: 0, saturation: 0, lightness: 0.501961, alpha: 1 }
123
+ * const example2 = srgbColorValueToHslColorValue({ red: 128, green: 128, blue: 128, alpha: 1 })
124
+ * ```
125
+ */
126
+ export const srgbColorValueToHslColorValue = (color: SrgbColorValue): HslColorValue => {
127
+ const normalizedColor = internalNormalizeSrgbColorValue(color)
128
+ const red = normalizedColor.red / 255
129
+ const green = normalizedColor.green / 255
130
+ const blue = normalizedColor.blue / 255
131
+ const max = Math.max(red, green, blue)
132
+ const min = Math.min(red, green, blue)
133
+ const delta = max - min
134
+ const lightness = (max + min) / 2
135
+ const saturation = delta === 0 ? 0 : delta / (1 - Math.abs((2 * lightness) - 1))
136
+ const hue = internalGetHueByNormalizedRgbChannels(red, green, blue, delta, max)
137
+
138
+ return internalNormalizeHslColorValue({
139
+ hue: internalRoundColorFloat(hue),
140
+ saturation: internalRoundColorFloat(saturation),
141
+ lightness: internalRoundColorFloat(lightness),
142
+ alpha: normalizedColor.alpha,
143
+ })
144
+ }
145
+
146
+ /**
147
+ * 将 HSL 颜色值转换为 sRGB 颜色值。
148
+ *
149
+ * @example
150
+ * ```
151
+ * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 }
152
+ * const example1 = hslColorValueToSrgbColorValue({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 })
153
+ *
154
+ * // Expect: { red: 128, green: 128, blue: 128, alpha: 1 }
155
+ * const example2 = hslColorValueToSrgbColorValue({ hue: 0, saturation: 0, lightness: 0.501961, alpha: 1 })
156
+ * ```
157
+ */
158
+ export const hslColorValueToSrgbColorValue = (color: HslColorValue): SrgbColorValue => {
159
+ const normalizedColor = internalNormalizeHslColorValue(color)
160
+ const chroma = (1 - Math.abs((2 * normalizedColor.lightness) - 1)) * normalizedColor.saturation
161
+ const channels = internalGetNormalizedRgbChannelsByHue(normalizedColor.hue, chroma)
162
+ const match = normalizedColor.lightness - (chroma / 2)
163
+
164
+ return internalNormalizeSrgbColorValue({
165
+ red: Math.round((channels.red + match) * 255),
166
+ green: Math.round((channels.green + match) * 255),
167
+ blue: Math.round((channels.blue + match) * 255),
168
+ alpha: normalizedColor.alpha,
169
+ })
170
+ }
171
+
172
+ /**
173
+ * 将 sRGB 颜色值转换为 HSV 颜色值。
174
+ *
175
+ * @example
176
+ * ```
177
+ * // Expect: { hue: 210, saturation: 0.666667, value: 0.6, alpha: 1 }
178
+ * const example1 = srgbColorValueToHsvColorValue({ red: 51, green: 102, blue: 153, alpha: 1 })
179
+ *
180
+ * // Expect: { hue: 0, saturation: 0, value: 0.501961, alpha: 1 }
181
+ * const example2 = srgbColorValueToHsvColorValue({ red: 128, green: 128, blue: 128, alpha: 1 })
182
+ * ```
183
+ */
184
+ export const srgbColorValueToHsvColorValue = (color: SrgbColorValue): HsvColorValue => {
185
+ const normalizedColor = internalNormalizeSrgbColorValue(color)
186
+ const red = normalizedColor.red / 255
187
+ const green = normalizedColor.green / 255
188
+ const blue = normalizedColor.blue / 255
189
+ const max = Math.max(red, green, blue)
190
+ const min = Math.min(red, green, blue)
191
+ const delta = max - min
192
+ const saturation = max === 0 ? 0 : delta / max
193
+ const hue = internalGetHueByNormalizedRgbChannels(red, green, blue, delta, max)
194
+
195
+ return internalNormalizeHsvColorValue({
196
+ hue: internalRoundColorFloat(hue),
197
+ saturation: internalRoundColorFloat(saturation),
198
+ value: internalRoundColorFloat(max),
199
+ alpha: normalizedColor.alpha,
200
+ })
201
+ }
202
+
203
+ /**
204
+ * 将 HSV 颜色值转换为 sRGB 颜色值。
205
+ *
206
+ * @example
207
+ * ```
208
+ * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 }
209
+ * const example1 = hsvColorValueToSrgbColorValue({ hue: 210, saturation: 0.666667, value: 0.6, alpha: 1 })
210
+ *
211
+ * // Expect: { red: 128, green: 128, blue: 128, alpha: 1 }
212
+ * const example2 = hsvColorValueToSrgbColorValue({ hue: 0, saturation: 0, value: 0.501961, alpha: 1 })
213
+ * ```
214
+ */
215
+ export const hsvColorValueToSrgbColorValue = (color: HsvColorValue): SrgbColorValue => {
216
+ const normalizedColor = internalNormalizeHsvColorValue(color)
217
+ const chroma = normalizedColor.value * normalizedColor.saturation
218
+ const channels = internalGetNormalizedRgbChannelsByHue(normalizedColor.hue, chroma)
219
+ const match = normalizedColor.value - chroma
220
+
221
+ return internalNormalizeSrgbColorValue({
222
+ red: Math.round((channels.red + match) * 255),
223
+ green: Math.round((channels.green + match) * 255),
224
+ blue: Math.round((channels.blue + match) * 255),
225
+ alpha: normalizedColor.alpha,
226
+ })
227
+ }