@chipmobilesdk/rn-theme 0.2.1 → 1.0.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 (42) hide show
  1. package/CHANGELOG.md +91 -23
  2. package/NOTICE +9 -0
  3. package/README.md +297 -154
  4. package/package.json +49 -50
  5. package/skills/sdk-rn-theme/SKILL.md +45 -30
  6. package/src/color/categorical.ts +33 -0
  7. package/src/color/contrast.ts +230 -0
  8. package/src/color/parse.ts +120 -0
  9. package/src/color/roles.ts +262 -0
  10. package/src/index.ts +23 -17
  11. package/src/integration/navigationTheme.ts +51 -0
  12. package/src/presets/byTone.ts +28 -0
  13. package/src/presets/layout.ts +38 -0
  14. package/src/presets/themePresets.ts +101 -0
  15. package/src/theme/buildThemes.ts +178 -0
  16. package/src/theme/errors.ts +34 -0
  17. package/src/theme/overrides.ts +142 -0
  18. package/src/theme/validateThemeConfig.ts +171 -0
  19. package/src/tokens/defaults.ts +103 -46
  20. package/src/tokens/typography.ts +144 -64
  21. package/src/types/brand.ts +16 -0
  22. package/src/types/colors.ts +80 -11
  23. package/src/types/index.ts +44 -5
  24. package/src/types/theme.ts +161 -0
  25. package/src/types/tokens.ts +47 -5
  26. package/src/types/typography.ts +46 -10
  27. package/src/unistyles/createUnistylesConfig.ts +165 -177
  28. package/src/unistyles/themeMode.ts +28 -0
  29. package/src/vendor/mcu/LICENSE +191 -0
  30. package/src/vendor/mcu/hct/cam16.ts +419 -0
  31. package/src/vendor/mcu/hct/hct.ts +216 -0
  32. package/src/vendor/mcu/hct/hct_solver.ts +557 -0
  33. package/src/vendor/mcu/hct/viewing_conditions.ts +121 -0
  34. package/src/vendor/mcu/index.ts +5 -0
  35. package/src/vendor/mcu/palettes/tonal_palette.ts +172 -0
  36. package/src/vendor/mcu/utils/color_utils.ts +302 -0
  37. package/src/vendor/mcu/utils/math_utils.ts +148 -0
  38. package/src/types/style.ts +0 -26
  39. package/src/unistyles/types.ts +0 -24
  40. package/src/unistyles/validateThemeConfig.ts +0 -154
  41. package/src/utils/colorGeneration.ts +0 -87
  42. package/src/utils/colorMath.ts +0 -55
@@ -0,0 +1,419 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2021 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ // Vendored from material-foundation/material-color-utilities v0.4.0
19
+ // (commit dd62982dfdefdfc9232ce8d76e122fb1c6d188ae, typescript/). Modified:
20
+ // relative import specifiers drop the `.js` suffix so Metro and Jest resolve them.
21
+ import * as utils from '../utils/color_utils';
22
+ import * as math from '../utils/math_utils';
23
+
24
+ import {ViewingConditions} from './viewing_conditions';
25
+
26
+ /**
27
+ * CAM16, a color appearance model. Colors are not just defined by their hex
28
+ * code, but rather, a hex code and viewing conditions.
29
+ *
30
+ * CAM16 instances also have coordinates in the CAM16-UCS space, called J*, a*,
31
+ * b*, or jstar, astar, bstar in code. CAM16-UCS is included in the CAM16
32
+ * specification, and should be used when measuring distances between colors.
33
+ *
34
+ * In traditional color spaces, a color can be identified solely by the
35
+ * observer's measurement of the color. Color appearance models such as CAM16
36
+ * also use information about the environment where the color was
37
+ * observed, known as the viewing conditions.
38
+ *
39
+ * For example, white under the traditional assumption of a midday sun white
40
+ * point is accurately measured as a slightly chromatic blue by CAM16. (roughly,
41
+ * hue 203, chroma 3, lightness 100)
42
+ */
43
+ export class Cam16 {
44
+ /**
45
+ * All of the CAM16 dimensions can be calculated from 3 of the dimensions, in
46
+ * the following combinations:
47
+ * - {j or q} and {c, m, or s} and hue
48
+ * - jstar, astar, bstar
49
+ * Prefer using a static method that constructs from 3 of those dimensions.
50
+ * This constructor is intended for those methods to use to return all
51
+ * possible dimensions.
52
+ *
53
+ * @param hue
54
+ * @param chroma informally, colorfulness / color intensity. like saturation
55
+ * in HSL, except perceptually accurate.
56
+ * @param j lightness
57
+ * @param q brightness; ratio of lightness to white point's lightness
58
+ * @param m colorfulness
59
+ * @param s saturation; ratio of chroma to white point's chroma
60
+ * @param jstar CAM16-UCS J coordinate
61
+ * @param astar CAM16-UCS a coordinate
62
+ * @param bstar CAM16-UCS b coordinate
63
+ */
64
+ constructor(
65
+ readonly hue: number, readonly chroma: number, readonly j: number,
66
+ readonly q: number, readonly m: number, readonly s: number,
67
+ readonly jstar: number, readonly astar: number, readonly bstar: number) {}
68
+
69
+ /**
70
+ * CAM16 instances also have coordinates in the CAM16-UCS space, called J*,
71
+ * a*, b*, or jstar, astar, bstar in code. CAM16-UCS is included in the CAM16
72
+ * specification, and is used to measure distances between colors.
73
+ */
74
+ distance(other: Cam16): number {
75
+ const dJ = this.jstar - other.jstar;
76
+ const dA = this.astar - other.astar;
77
+ const dB = this.bstar - other.bstar;
78
+ const dEPrime = Math.sqrt(dJ * dJ + dA * dA + dB * dB);
79
+ const dE = 1.41 * Math.pow(dEPrime, 0.63);
80
+ return dE;
81
+ }
82
+
83
+ /**
84
+ * @param argb ARGB representation of a color.
85
+ * @return CAM16 color, assuming the color was viewed in default viewing
86
+ * conditions.
87
+ */
88
+ static fromInt(argb: number): Cam16 {
89
+ return Cam16.fromIntInViewingConditions(argb, ViewingConditions.DEFAULT);
90
+ }
91
+
92
+ /**
93
+ * @param argb ARGB representation of a color.
94
+ * @param viewingConditions Information about the environment where the color
95
+ * was observed.
96
+ * @return CAM16 color.
97
+ */
98
+ static fromIntInViewingConditions(
99
+ argb: number, viewingConditions: ViewingConditions): Cam16 {
100
+ const red = (argb & 0x00ff0000) >> 16;
101
+ const green = (argb & 0x0000ff00) >> 8;
102
+ const blue = (argb & 0x000000ff);
103
+ const redL = utils.linearized(red);
104
+ const greenL = utils.linearized(green);
105
+ const blueL = utils.linearized(blue);
106
+ const x = 0.41233895 * redL + 0.35762064 * greenL + 0.18051042 * blueL;
107
+ const y = 0.2126 * redL + 0.7152 * greenL + 0.0722 * blueL;
108
+ const z = 0.01932141 * redL + 0.11916382 * greenL + 0.95034478 * blueL;
109
+
110
+ const rC = 0.401288 * x + 0.650173 * y - 0.051461 * z;
111
+ const gC = -0.250268 * x + 1.204414 * y + 0.045854 * z;
112
+ const bC = -0.002079 * x + 0.048952 * y + 0.953127 * z;
113
+
114
+ const rD = viewingConditions.rgbD[0] * rC;
115
+ const gD = viewingConditions.rgbD[1] * gC;
116
+ const bD = viewingConditions.rgbD[2] * bC;
117
+
118
+ const rAF = Math.pow((viewingConditions.fl * Math.abs(rD)) / 100.0, 0.42);
119
+ const gAF = Math.pow((viewingConditions.fl * Math.abs(gD)) / 100.0, 0.42);
120
+ const bAF = Math.pow((viewingConditions.fl * Math.abs(bD)) / 100.0, 0.42);
121
+
122
+ const rA = (math.signum(rD) * 400.0 * rAF) / (rAF + 27.13);
123
+ const gA = (math.signum(gD) * 400.0 * gAF) / (gAF + 27.13);
124
+ const bA = (math.signum(bD) * 400.0 * bAF) / (bAF + 27.13);
125
+
126
+ const a = (11.0 * rA + -12.0 * gA + bA) / 11.0;
127
+ const b = (rA + gA - 2.0 * bA) / 9.0;
128
+ const u = (20.0 * rA + 20.0 * gA + 21.0 * bA) / 20.0;
129
+ const p2 = (40.0 * rA + 20.0 * gA + bA) / 20.0;
130
+ const atan2 = Math.atan2(b, a);
131
+ const atanDegrees = (atan2 * 180.0) / Math.PI;
132
+ const hue = math.sanitizeDegreesDouble(atanDegrees);
133
+ const hueRadians = (hue * Math.PI) / 180.0;
134
+
135
+ const ac = p2 * viewingConditions.nbb;
136
+ const j = 100.0 *
137
+ Math.pow(
138
+ ac / viewingConditions.aw,
139
+ viewingConditions.c * viewingConditions.z);
140
+ const q = (4.0 / viewingConditions.c) * Math.sqrt(j / 100.0) *
141
+ (viewingConditions.aw + 4.0) * viewingConditions.fLRoot;
142
+ const huePrime = hue < 20.14 ? hue + 360 : hue;
143
+ const eHue = 0.25 * (Math.cos((huePrime * Math.PI) / 180.0 + 2.0) + 3.8);
144
+ const p1 =
145
+ (50000.0 / 13.0) * eHue * viewingConditions.nc * viewingConditions.ncb;
146
+ const t = (p1 * Math.sqrt(a * a + b * b)) / (u + 0.305);
147
+ const alpha = Math.pow(t, 0.9) *
148
+ Math.pow(1.64 - Math.pow(0.29, viewingConditions.n), 0.73);
149
+ const c = alpha * Math.sqrt(j / 100.0);
150
+ const m = c * viewingConditions.fLRoot;
151
+ const s = 50.0 *
152
+ Math.sqrt((alpha * viewingConditions.c) / (viewingConditions.aw + 4.0));
153
+ const jstar = ((1.0 + 100.0 * 0.007) * j) / (1.0 + 0.007 * j);
154
+ const mstar = (1.0 / 0.0228) * Math.log(1.0 + 0.0228 * m);
155
+ const astar = mstar * Math.cos(hueRadians);
156
+ const bstar = mstar * Math.sin(hueRadians);
157
+
158
+ return new Cam16(hue, c, j, q, m, s, jstar, astar, bstar);
159
+ }
160
+
161
+ /**
162
+ * @param j CAM16 lightness
163
+ * @param c CAM16 chroma
164
+ * @param h CAM16 hue
165
+ */
166
+ static fromJch(j: number, c: number, h: number): Cam16 {
167
+ return Cam16.fromJchInViewingConditions(j, c, h, ViewingConditions.DEFAULT);
168
+ }
169
+
170
+ /**
171
+ * @param j CAM16 lightness
172
+ * @param c CAM16 chroma
173
+ * @param h CAM16 hue
174
+ * @param viewingConditions Information about the environment where the color
175
+ * was observed.
176
+ */
177
+ static fromJchInViewingConditions(
178
+ j: number, c: number, h: number,
179
+ viewingConditions: ViewingConditions): Cam16 {
180
+ const q = (4.0 / viewingConditions.c) * Math.sqrt(j / 100.0) *
181
+ (viewingConditions.aw + 4.0) * viewingConditions.fLRoot;
182
+ const m = c * viewingConditions.fLRoot;
183
+ const alpha = c / Math.sqrt(j / 100.0);
184
+ const s = 50.0 *
185
+ Math.sqrt((alpha * viewingConditions.c) / (viewingConditions.aw + 4.0));
186
+ const hueRadians = (h * Math.PI) / 180.0;
187
+ const jstar = ((1.0 + 100.0 * 0.007) * j) / (1.0 + 0.007 * j);
188
+ const mstar = (1.0 / 0.0228) * Math.log(1.0 + 0.0228 * m);
189
+ const astar = mstar * Math.cos(hueRadians);
190
+ const bstar = mstar * Math.sin(hueRadians);
191
+ return new Cam16(h, c, j, q, m, s, jstar, astar, bstar);
192
+ }
193
+
194
+ /**
195
+ * @param jstar CAM16-UCS lightness.
196
+ * @param astar CAM16-UCS a dimension. Like a* in L*a*b*, it is a Cartesian
197
+ * coordinate on the Y axis.
198
+ * @param bstar CAM16-UCS b dimension. Like a* in L*a*b*, it is a Cartesian
199
+ * coordinate on the X axis.
200
+ */
201
+ static fromUcs(jstar: number, astar: number, bstar: number): Cam16 {
202
+ return Cam16.fromUcsInViewingConditions(
203
+ jstar, astar, bstar, ViewingConditions.DEFAULT);
204
+ }
205
+
206
+ /**
207
+ * @param jstar CAM16-UCS lightness.
208
+ * @param astar CAM16-UCS a dimension. Like a* in L*a*b*, it is a Cartesian
209
+ * coordinate on the Y axis.
210
+ * @param bstar CAM16-UCS b dimension. Like a* in L*a*b*, it is a Cartesian
211
+ * coordinate on the X axis.
212
+ * @param viewingConditions Information about the environment where the color
213
+ * was observed.
214
+ */
215
+ static fromUcsInViewingConditions(
216
+ jstar: number, astar: number, bstar: number,
217
+ viewingConditions: ViewingConditions): Cam16 {
218
+ const a = astar;
219
+ const b = bstar;
220
+ const m = Math.sqrt(a * a + b * b);
221
+ const M = (Math.exp(m * 0.0228) - 1.0) / 0.0228;
222
+ const c = M / viewingConditions.fLRoot;
223
+ let h = Math.atan2(b, a) * (180.0 / Math.PI);
224
+ if (h < 0.0) {
225
+ h += 360.0;
226
+ }
227
+ const j = jstar / (1 - (jstar - 100) * 0.007);
228
+ return Cam16.fromJchInViewingConditions(j, c, h, viewingConditions);
229
+ }
230
+
231
+ /**
232
+ * @return ARGB representation of color, assuming the color was viewed in
233
+ * default viewing conditions, which are near-identical to the default
234
+ * viewing conditions for sRGB.
235
+ */
236
+ toInt(): number {
237
+ return this.viewed(ViewingConditions.DEFAULT);
238
+ }
239
+
240
+ /**
241
+ * @param viewingConditions Information about the environment where the color
242
+ * will be viewed.
243
+ * @return ARGB representation of color
244
+ */
245
+ viewed(viewingConditions: ViewingConditions): number {
246
+ const alpha = this.chroma === 0.0 || this.j === 0.0 ?
247
+ 0.0 :
248
+ this.chroma / Math.sqrt(this.j / 100.0);
249
+
250
+ const t = Math.pow(
251
+ alpha / Math.pow(1.64 - Math.pow(0.29, viewingConditions.n), 0.73),
252
+ 1.0 / 0.9);
253
+ const hRad = (this.hue * Math.PI) / 180.0;
254
+
255
+ const eHue = 0.25 * (Math.cos(hRad + 2.0) + 3.8);
256
+ const ac = viewingConditions.aw *
257
+ Math.pow(
258
+ this.j / 100.0, 1.0 / viewingConditions.c / viewingConditions.z);
259
+ const p1 =
260
+ eHue * (50000.0 / 13.0) * viewingConditions.nc * viewingConditions.ncb;
261
+ const p2 = ac / viewingConditions.nbb;
262
+
263
+ const hSin = Math.sin(hRad);
264
+ const hCos = Math.cos(hRad);
265
+
266
+ const gamma = (23.0 * (p2 + 0.305) * t) /
267
+ (23.0 * p1 + 11.0 * t * hCos + 108.0 * t * hSin);
268
+ const a = gamma * hCos;
269
+ const b = gamma * hSin;
270
+ const rA = (460.0 * p2 + 451.0 * a + 288.0 * b) / 1403.0;
271
+ const gA = (460.0 * p2 - 891.0 * a - 261.0 * b) / 1403.0;
272
+ const bA = (460.0 * p2 - 220.0 * a - 6300.0 * b) / 1403.0;
273
+
274
+ const rCBase = Math.max(0, (27.13 * Math.abs(rA)) / (400.0 - Math.abs(rA)));
275
+ const rC = math.signum(rA) * (100.0 / viewingConditions.fl) *
276
+ Math.pow(rCBase, 1.0 / 0.42);
277
+ const gCBase = Math.max(0, (27.13 * Math.abs(gA)) / (400.0 - Math.abs(gA)));
278
+ const gC = math.signum(gA) * (100.0 / viewingConditions.fl) *
279
+ Math.pow(gCBase, 1.0 / 0.42);
280
+ const bCBase = Math.max(0, (27.13 * Math.abs(bA)) / (400.0 - Math.abs(bA)));
281
+ const bC = math.signum(bA) * (100.0 / viewingConditions.fl) *
282
+ Math.pow(bCBase, 1.0 / 0.42);
283
+ const rF = rC / viewingConditions.rgbD[0];
284
+ const gF = gC / viewingConditions.rgbD[1];
285
+ const bF = bC / viewingConditions.rgbD[2];
286
+
287
+ const x = 1.86206786 * rF - 1.01125463 * gF + 0.14918677 * bF;
288
+ const y = 0.38752654 * rF + 0.62144744 * gF - 0.00897398 * bF;
289
+ const z = -0.01584150 * rF - 0.03412294 * gF + 1.04996444 * bF;
290
+
291
+ const argb = utils.argbFromXyz(x, y, z);
292
+ return argb;
293
+ }
294
+
295
+ /// Given color expressed in XYZ and viewed in [viewingConditions], convert to
296
+ /// CAM16.
297
+ static fromXyzInViewingConditions(
298
+ x: number, y: number, z: number,
299
+ viewingConditions: ViewingConditions): Cam16 {
300
+ // Transform XYZ to 'cone'/'rgb' responses
301
+
302
+ const rC = 0.401288 * x + 0.650173 * y - 0.051461 * z;
303
+ const gC = -0.250268 * x + 1.204414 * y + 0.045854 * z;
304
+ const bC = -0.002079 * x + 0.048952 * y + 0.953127 * z;
305
+
306
+ // Discount illuminant
307
+ const rD = viewingConditions.rgbD[0] * rC;
308
+ const gD = viewingConditions.rgbD[1] * gC;
309
+ const bD = viewingConditions.rgbD[2] * bC;
310
+
311
+ // chromatic adaptation
312
+ const rAF = Math.pow(viewingConditions.fl * Math.abs(rD) / 100.0, 0.42);
313
+ const gAF = Math.pow(viewingConditions.fl * Math.abs(gD) / 100.0, 0.42);
314
+ const bAF = Math.pow(viewingConditions.fl * Math.abs(bD) / 100.0, 0.42);
315
+ const rA = math.signum(rD) * 400.0 * rAF / (rAF + 27.13);
316
+ const gA = math.signum(gD) * 400.0 * gAF / (gAF + 27.13);
317
+ const bA = math.signum(bD) * 400.0 * bAF / (bAF + 27.13);
318
+
319
+ // redness-greenness
320
+ const a = (11.0 * rA + -12.0 * gA + bA) / 11.0;
321
+ // yellowness-blueness
322
+ const b = (rA + gA - 2.0 * bA) / 9.0;
323
+
324
+ // auxiliary components
325
+ const u = (20.0 * rA + 20.0 * gA + 21.0 * bA) / 20.0;
326
+ const p2 = (40.0 * rA + 20.0 * gA + bA) / 20.0;
327
+
328
+ // hue
329
+ const atan2 = Math.atan2(b, a);
330
+ const atanDegrees = atan2 * 180.0 / Math.PI;
331
+ const hue = atanDegrees < 0 ? atanDegrees + 360.0 :
332
+ atanDegrees >= 360 ? atanDegrees - 360 :
333
+ atanDegrees;
334
+ const hueRadians = hue * Math.PI / 180.0;
335
+
336
+ // achromatic response to color
337
+ const ac = p2 * viewingConditions.nbb;
338
+
339
+ // CAM16 lightness and brightness
340
+ const J = 100.0 *
341
+ Math.pow(
342
+ ac / viewingConditions.aw,
343
+ viewingConditions.c * viewingConditions.z);
344
+ const Q = (4.0 / viewingConditions.c) * Math.sqrt(J / 100.0) *
345
+ (viewingConditions.aw + 4.0) * (viewingConditions.fLRoot);
346
+
347
+ const huePrime = (hue < 20.14) ? hue + 360 : hue;
348
+ const eHue =
349
+ (1.0 / 4.0) * (Math.cos(huePrime * Math.PI / 180.0 + 2.0) + 3.8);
350
+ const p1 =
351
+ 50000.0 / 13.0 * eHue * viewingConditions.nc * viewingConditions.ncb;
352
+ const t = p1 * Math.sqrt(a * a + b * b) / (u + 0.305);
353
+ const alpha = Math.pow(t, 0.9) *
354
+ Math.pow(1.64 - Math.pow(0.29, viewingConditions.n), 0.73);
355
+ // CAM16 chroma, colorfulness, chroma
356
+ const C = alpha * Math.sqrt(J / 100.0);
357
+ const M = C * viewingConditions.fLRoot;
358
+ const s = 50.0 *
359
+ Math.sqrt((alpha * viewingConditions.c) / (viewingConditions.aw + 4.0));
360
+
361
+ // CAM16-UCS components
362
+ const jstar = (1.0 + 100.0 * 0.007) * J / (1.0 + 0.007 * J);
363
+ const mstar = Math.log(1.0 + 0.0228 * M) / 0.0228;
364
+ const astar = mstar * Math.cos(hueRadians);
365
+ const bstar = mstar * Math.sin(hueRadians);
366
+ return new Cam16(hue, C, J, Q, M, s, jstar, astar, bstar);
367
+ }
368
+
369
+ /// XYZ representation of CAM16 seen in [viewingConditions].
370
+ xyzInViewingConditions(viewingConditions: ViewingConditions): number[] {
371
+ const alpha = (this.chroma === 0.0 || this.j === 0.0) ?
372
+ 0.0 :
373
+ this.chroma / Math.sqrt(this.j / 100.0);
374
+
375
+ const t = Math.pow(
376
+ alpha / Math.pow(1.64 - Math.pow(0.29, viewingConditions.n), 0.73),
377
+ 1.0 / 0.9);
378
+ const hRad = this.hue * Math.PI / 180.0;
379
+
380
+ const eHue = 0.25 * (Math.cos(hRad + 2.0) + 3.8);
381
+ const ac = viewingConditions.aw *
382
+ Math.pow(
383
+ this.j / 100.0, 1.0 / viewingConditions.c / viewingConditions.z);
384
+ const p1 =
385
+ eHue * (50000.0 / 13.0) * viewingConditions.nc * viewingConditions.ncb;
386
+
387
+ const p2 = (ac / viewingConditions.nbb);
388
+
389
+ const hSin = Math.sin(hRad);
390
+ const hCos = Math.cos(hRad);
391
+
392
+ const gamma = 23.0 * (p2 + 0.305) * t /
393
+ (23.0 * p1 + 11 * t * hCos + 108.0 * t * hSin);
394
+ const a = gamma * hCos;
395
+ const b = gamma * hSin;
396
+ const rA = (460.0 * p2 + 451.0 * a + 288.0 * b) / 1403.0;
397
+ const gA = (460.0 * p2 - 891.0 * a - 261.0 * b) / 1403.0;
398
+ const bA = (460.0 * p2 - 220.0 * a - 6300.0 * b) / 1403.0;
399
+
400
+ const rCBase = Math.max(0, (27.13 * Math.abs(rA)) / (400.0 - Math.abs(rA)));
401
+ const rC = math.signum(rA) * (100.0 / viewingConditions.fl) *
402
+ Math.pow(rCBase, 1.0 / 0.42);
403
+ const gCBase = Math.max(0, (27.13 * Math.abs(gA)) / (400.0 - Math.abs(gA)));
404
+ const gC = math.signum(gA) * (100.0 / viewingConditions.fl) *
405
+ Math.pow(gCBase, 1.0 / 0.42);
406
+ const bCBase = Math.max(0, (27.13 * Math.abs(bA)) / (400.0 - Math.abs(bA)));
407
+ const bC = math.signum(bA) * (100.0 / viewingConditions.fl) *
408
+ Math.pow(bCBase, 1.0 / 0.42);
409
+ const rF = rC / viewingConditions.rgbD[0];
410
+ const gF = gC / viewingConditions.rgbD[1];
411
+ const bF = bC / viewingConditions.rgbD[2];
412
+
413
+ const x = 1.86206786 * rF - 1.01125463 * gF + 0.14918677 * bF;
414
+ const y = 0.38752654 * rF + 0.62144744 * gF - 0.00897398 * bF;
415
+ const z = -0.01584150 * rF - 0.03412294 * gF + 1.04996444 * bF;
416
+
417
+ return [x, y, z];
418
+ }
419
+ }
@@ -0,0 +1,216 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2021 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ // Vendored from material-foundation/material-color-utilities v0.4.0
19
+ // (commit dd62982dfdefdfc9232ce8d76e122fb1c6d188ae, typescript/). Modified:
20
+ // relative import specifiers drop the `.js` suffix so Metro and Jest resolve them.
21
+ /**
22
+ * A color system built using CAM16 hue and chroma, and L* from
23
+ * L*a*b*.
24
+ *
25
+ * Using L* creates a link between the color system, contrast, and thus
26
+ * accessibility. Contrast ratio depends on relative luminance, or Y in the XYZ
27
+ * color space. L*, or perceptual luminance can be calculated from Y.
28
+ *
29
+ * Unlike Y, L* is linear to human perception, allowing trivial creation of
30
+ * accurate color tones.
31
+ *
32
+ * Unlike contrast ratio, measuring contrast in L* is linear, and simple to
33
+ * calculate. A difference of 40 in HCT tone guarantees a contrast ratio >= 3.0,
34
+ * and a difference of 50 guarantees a contrast ratio >= 4.5.
35
+ */
36
+
37
+ import * as utils from '../utils/color_utils';
38
+
39
+ import {Cam16} from './cam16';
40
+ import {HctSolver} from './hct_solver';
41
+ import {ViewingConditions} from './viewing_conditions';
42
+
43
+
44
+ /**
45
+ * HCT, hue, chroma, and tone. A color system that provides a perceptually
46
+ * accurate color measurement system that can also accurately render what colors
47
+ * will appear as in different lighting environments.
48
+ */
49
+ export class Hct {
50
+ /**
51
+ * @param hue 0 <= hue < 360; invalid values are corrected.
52
+ * @param chroma 0 <= chroma < ?; Informally, colorfulness. The color
53
+ * returned may be lower than the requested chroma. Chroma has a different
54
+ * maximum for any given hue and tone.
55
+ * @param tone 0 <= tone <= 100; invalid values are corrected.
56
+ * @return HCT representation of a color in default viewing conditions.
57
+ */
58
+
59
+ internalHue: number;
60
+ internalChroma: number;
61
+ internalTone: number;
62
+
63
+ static from(hue: number, chroma: number, tone: number) {
64
+ return new Hct(HctSolver.solveToInt(hue, chroma, tone));
65
+ }
66
+
67
+ /**
68
+ * @param argb ARGB representation of a color.
69
+ * @return HCT representation of a color in default viewing conditions
70
+ */
71
+ static fromInt(argb: number) {
72
+ return new Hct(argb);
73
+ }
74
+
75
+ toInt(): number {
76
+ return this.argb;
77
+ }
78
+
79
+ /**
80
+ * A number, in degrees, representing ex. red, orange, yellow, etc.
81
+ * Ranges from 0 <= hue < 360.
82
+ */
83
+ get hue(): number {
84
+ return this.internalHue;
85
+ }
86
+
87
+ /**
88
+ * @param newHue 0 <= newHue < 360; invalid values are corrected.
89
+ * Chroma may decrease because chroma has a different maximum for any given
90
+ * hue and tone.
91
+ */
92
+ set hue(newHue: number) {
93
+ this.setInternalState(
94
+ HctSolver.solveToInt(
95
+ newHue,
96
+ this.internalChroma,
97
+ this.internalTone,
98
+ ),
99
+ );
100
+ }
101
+
102
+ get chroma(): number {
103
+ return this.internalChroma;
104
+ }
105
+
106
+ /**
107
+ * @param newChroma 0 <= newChroma < ?
108
+ * Chroma may decrease because chroma has a different maximum for any given
109
+ * hue and tone.
110
+ */
111
+ set chroma(newChroma: number) {
112
+ this.setInternalState(
113
+ HctSolver.solveToInt(
114
+ this.internalHue,
115
+ newChroma,
116
+ this.internalTone,
117
+ ),
118
+ );
119
+ }
120
+
121
+ /** Lightness. Ranges from 0 to 100. */
122
+ get tone(): number {
123
+ return this.internalTone;
124
+ }
125
+
126
+ /**
127
+ * @param newTone 0 <= newTone <= 100; invalid valids are corrected.
128
+ * Chroma may decrease because chroma has a different maximum for any given
129
+ * hue and tone.
130
+ */
131
+ set tone(newTone: number) {
132
+ this.setInternalState(
133
+ HctSolver.solveToInt(
134
+ this.internalHue,
135
+ this.internalChroma,
136
+ newTone,
137
+ ),
138
+ );
139
+ }
140
+
141
+ /** Sets a property of the Hct object. */
142
+ setValue(propertyName: string, value: number) {
143
+ (this as any)[propertyName] = value;
144
+ }
145
+
146
+ toString(): string {
147
+ return `HCT(${this.hue.toFixed(0)}, ${this.chroma.toFixed(0)}, ${
148
+ this.tone.toFixed(0)})`;
149
+ }
150
+
151
+ static isBlue(hue: number): boolean {
152
+ return hue >= 250 && hue < 270;
153
+ }
154
+
155
+ static isYellow(hue: number): boolean {
156
+ return hue >= 105 && hue < 125;
157
+ }
158
+
159
+ static isCyan(hue: number): boolean {
160
+ return hue >= 170 && hue < 207;
161
+ }
162
+
163
+ private constructor(private argb: number) {
164
+ const cam = Cam16.fromInt(argb);
165
+ this.internalHue = cam.hue;
166
+ this.internalChroma = cam.chroma;
167
+ this.internalTone = utils.lstarFromArgb(argb);
168
+ this.argb = argb;
169
+ }
170
+
171
+ private setInternalState(argb: number) {
172
+ const cam = Cam16.fromInt(argb);
173
+ this.internalHue = cam.hue;
174
+ this.internalChroma = cam.chroma;
175
+ this.internalTone = utils.lstarFromArgb(argb);
176
+ this.argb = argb;
177
+ }
178
+
179
+ /**
180
+ * Translates a color into different [ViewingConditions].
181
+ *
182
+ * Colors change appearance. They look different with lights on versus off,
183
+ * the same color, as in hex code, on white looks different when on black.
184
+ * This is called color relativity, most famously explicated by Josef Albers
185
+ * in Interaction of Color.
186
+ *
187
+ * In color science, color appearance models can account for this and
188
+ * calculate the appearance of a color in different settings. HCT is based on
189
+ * CAM16, a color appearance model, and uses it to make these calculations.
190
+ *
191
+ * See [ViewingConditions.make] for parameters affecting color appearance.
192
+ */
193
+ inViewingConditions(vc: ViewingConditions): Hct {
194
+ // 1. Use CAM16 to find XYZ coordinates of color in specified VC.
195
+ const cam = Cam16.fromInt(this.toInt());
196
+ const viewedInVc = cam.xyzInViewingConditions(vc);
197
+
198
+ // 2. Create CAM16 of those XYZ coordinates in default VC.
199
+ const recastInVc = Cam16.fromXyzInViewingConditions(
200
+ viewedInVc[0],
201
+ viewedInVc[1],
202
+ viewedInVc[2],
203
+ ViewingConditions.make(),
204
+ );
205
+
206
+ // 3. Create HCT from:
207
+ // - CAM16 using default VC with XYZ coordinates in specified VC.
208
+ // - L* converted from Y in XYZ coordinates in specified VC.
209
+ const recastHct = Hct.from(
210
+ recastInVc.hue,
211
+ recastInVc.chroma,
212
+ utils.lstarFromY(viewedInVc[1]),
213
+ );
214
+ return recastHct;
215
+ }
216
+ }