1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | const d3 = require('./d3.js');
|
14 |
|
15 | const { catmullRom2bezier, prepareCurve } = require('./curve.js');
|
16 | const { color } = require('./d3.js');
|
17 |
|
18 | function smoothScale(ColorsArray, domains, space) {
|
19 | const points = space.channels.map(() => []);
|
20 | ColorsArray.forEach((color, i) =>
|
21 | points.forEach((point, j) =>
|
22 | point.push(domains[i], color[space.channels[j]])
|
23 | )
|
24 | );
|
25 | if (space.name == "hcl") {
|
26 | const point = points[1];
|
27 | for (let i = 1; i < point.length; i += 2) {
|
28 | if (isNaN(point[i])) {
|
29 | point[i] = 0;
|
30 | }
|
31 | }
|
32 | }
|
33 | points.forEach(point => {
|
34 | const nans = [];
|
35 |
|
36 | for (let i = 1; i < point.length; i += 2) {
|
37 | if (isNaN(point[i])) {
|
38 | nans.push(i);
|
39 | } else {
|
40 | nans.forEach(j => point[j] = point[i]);
|
41 | nans.length = 0;
|
42 | break;
|
43 | }
|
44 | }
|
45 |
|
46 | if (nans.length) {
|
47 |
|
48 | const safeJChHue = d3.jch("#ccc").h;
|
49 | nans.forEach(j => point[j] = safeJChHue);
|
50 | }
|
51 | nans.length = 0;
|
52 |
|
53 | for (let i = point.length - 1; i > 0; i -= 2) {
|
54 | if (isNaN(point[i])) {
|
55 | nans.push(i);
|
56 | } else {
|
57 | nans.forEach(j => point[j] = point[i]);
|
58 | break;
|
59 | }
|
60 | }
|
61 |
|
62 | for (let i = 1; i < point.length; i += 2) {
|
63 | if (isNaN(point[i])) {
|
64 | point.splice(i - 1, 2);
|
65 | i -= 2;
|
66 | }
|
67 | }
|
68 |
|
69 | if (space.name in {hcl: 1, hsl: 1, hsluv: 1, hsv: 1, jch: 1}) {
|
70 | let prev = point[1];
|
71 | let addon = 0;
|
72 | for (let i = 3; i < point.length; i += 2) {
|
73 | const p = point[i] + addon;
|
74 | const zero = Math.abs(prev - p);
|
75 | const plus = Math.abs(prev - (p + 360));
|
76 | const minus = Math.abs(prev - (p - 360));
|
77 | if (plus < zero && plus < minus) {
|
78 | addon += 360;
|
79 | }
|
80 | if (minus < zero && minus < plus) {
|
81 | addon -= 360;
|
82 | }
|
83 | point[i] += addon;
|
84 | prev = point[i];
|
85 | }
|
86 | }
|
87 | })
|
88 | const prep = points.map(point =>
|
89 | catmullRom2bezier(point).map(curve =>
|
90 | prepareCurve(...curve)
|
91 | )
|
92 | );
|
93 | return d => {
|
94 | const ch = prep.map(p => {
|
95 | for (let i = 0; i < p.length; i++) {
|
96 | const res = p[i](d);
|
97 | if (res != null) {
|
98 | return res;
|
99 | }
|
100 | }
|
101 | });
|
102 |
|
103 | if (space.name == 'jch' && ch[1] < 0) {
|
104 | ch[1] = 0;
|
105 | }
|
106 |
|
107 | return d3[space.name](...ch) + "";
|
108 | };
|
109 | }
|
110 |
|
111 | const colorSpaces = {
|
112 | CAM02: {
|
113 | name: 'jab',
|
114 | channels: ['J', 'a', 'b'],
|
115 | interpolator: d3.interpolateJab,
|
116 | function: d3.jab
|
117 | },
|
118 | CAM02p: {
|
119 | name: 'jch',
|
120 | channels: ['J', 'C', 'h'],
|
121 | interpolator: d3.interpolateJch,
|
122 | function: d3.jch
|
123 | },
|
124 | LCH: {
|
125 | name: 'lch',
|
126 | channels: ['h', 'c', 'l'],
|
127 | interpolator: d3.interpolateHcl,
|
128 | white: d3.hcl(NaN, 0, 100),
|
129 | black: d3.hcl(NaN, 0, 0),
|
130 | function: d3.hcl
|
131 | },
|
132 | LAB: {
|
133 | name: 'lab',
|
134 | channels: ['l', 'a', 'b'],
|
135 | interpolator: d3.interpolateLab,
|
136 | function: d3.lab
|
137 | },
|
138 | HSL: {
|
139 | name: 'hsl',
|
140 | channels: ['h', 's', 'l'],
|
141 | interpolator: d3.interpolateHsl,
|
142 | function: d3.hsl
|
143 | },
|
144 | HSLuv: {
|
145 | name: 'hsluv',
|
146 | channels: ['l', 'u', 'v'],
|
147 | interpolator: d3.interpolateHsluv,
|
148 | white: d3.hsluv(NaN, NaN, 100),
|
149 | black: d3.hsluv(NaN, NaN, 0),
|
150 | function: d3.hsluv
|
151 | },
|
152 | RGB: {
|
153 | name: 'rgb',
|
154 | channels: ['r', 'g', 'b'],
|
155 | interpolator: d3.interpolateRgb,
|
156 | function: d3.rgb
|
157 | },
|
158 | HSV: {
|
159 | name: 'hsv',
|
160 | channels: ['h', 's', 'v'],
|
161 | interpolator: d3.interpolateHsv,
|
162 | function: d3.hsv
|
163 | },
|
164 | HEX: {
|
165 | name: 'hex',
|
166 | channels: ['r', 'g', 'b'],
|
167 | interpolator: d3.interpolateRgb,
|
168 | function: d3.rgb
|
169 | }
|
170 | };
|
171 |
|
172 | function cArray(c) {
|
173 | const color = d3.hsluv(c);
|
174 | const L = color.l;
|
175 | const U = color.u;
|
176 | const V = color.v;
|
177 |
|
178 | return [L, U, V];
|
179 | }
|
180 |
|
181 | function removeDuplicates(originalArray, prop) {
|
182 | var newArray = [];
|
183 | var lookupObject = {};
|
184 |
|
185 | for(var i in originalArray) {
|
186 | lookupObject[originalArray[i][prop]] = originalArray[i];
|
187 | }
|
188 |
|
189 | for(i in lookupObject) {
|
190 | newArray.push(lookupObject[i]);
|
191 | }
|
192 | return newArray;
|
193 | }
|
194 |
|
195 | function createScale({
|
196 | swatches,
|
197 | colorKeys,
|
198 | colorspace = 'LAB',
|
199 | shift = 1,
|
200 | fullScale = true,
|
201 | smooth = false
|
202 | } = {}) {
|
203 | const space = colorSpaces[colorspace];
|
204 | if (!space) {
|
205 | throw new Error(`Colorspace “${colorspace}” not supported`);
|
206 | }
|
207 |
|
208 | let domains = colorKeys
|
209 | .map(key => swatches - swatches * (d3.hsluv(key).v / 100))
|
210 | .sort((a, b) => a - b)
|
211 | .concat(swatches);
|
212 |
|
213 | domains.unshift(0);
|
214 |
|
215 |
|
216 | let sqrtDomains = d3.scalePow()
|
217 | .exponent(shift)
|
218 | .domain([1, swatches])
|
219 | .range([1, swatches]);
|
220 |
|
221 | sqrtDomains = domains.map((d) => {
|
222 | if (sqrtDomains(d) < 0) {
|
223 | return 0;
|
224 | }
|
225 | return sqrtDomains(d);
|
226 | });
|
227 |
|
228 |
|
229 | domains = sqrtDomains;
|
230 |
|
231 | let sortedColor = colorKeys
|
232 |
|
233 | .map((c, i) => { return { colorKeys: cArray(c), index: i } })
|
234 |
|
235 | .sort((c1, c2) => c2.colorKeys[2] - c1.colorKeys[2])
|
236 |
|
237 | .map(data => colorKeys[data.index]);
|
238 |
|
239 | let inverseSortedColor = colorKeys
|
240 |
|
241 | .map((c, i) => { return {colorKeys: cArray(c), index: i} })
|
242 |
|
243 | .sort((c1, c2) => c1.colorKeys[2] - c2.colorKeys[2])
|
244 |
|
245 | .map(data => colorKeys[data.index]);
|
246 |
|
247 | let ColorsArray = [];
|
248 |
|
249 | let scale;
|
250 | if (fullScale) {
|
251 | ColorsArray = [space.white || '#fff', ...sortedColor, space.black || '#000'];
|
252 | } else {
|
253 | ColorsArray = sortedColor;
|
254 | }
|
255 | const stringColors = ColorsArray;
|
256 | ColorsArray = ColorsArray.map(d => d3[space.name](d));
|
257 | if (space.name == 'hcl') {
|
258 |
|
259 | ColorsArray.forEach(c => c.c = isNaN(c.c) ? 0 : c.c);
|
260 | }
|
261 | if (space.name == 'jch') {
|
262 |
|
263 |
|
264 | for (let i = 0; i < stringColors.length; i++) {
|
265 | const color = d3.hcl(stringColors[i]);
|
266 | if (!color.c) {
|
267 | ColorsArray[i].h = NaN;
|
268 | }
|
269 | }
|
270 | }
|
271 |
|
272 | if (smooth) {
|
273 | scale = smoothScale(ColorsArray, domains, space);
|
274 | } else {
|
275 | scale = d3.scaleLinear()
|
276 | .range(ColorsArray)
|
277 | .domain(domains)
|
278 | .interpolate(space.interpolator);
|
279 | }
|
280 |
|
281 | let Colors = d3.range(swatches).map(d => scale(d));
|
282 |
|
283 | let colors = Colors.filter(el => el != null);
|
284 |
|
285 |
|
286 | let colorsHex = [];
|
287 | for (let i = 0; i < colors.length; i++) {
|
288 | colorsHex.push(d3.rgb(colors[i]).formatHex());
|
289 | }
|
290 |
|
291 | return {
|
292 | colorKeys: colorKeys,
|
293 | colorspace: colorspace,
|
294 | shift: shift,
|
295 | colors: colors,
|
296 | scale: scale,
|
297 | colorsHex: colorsHex
|
298 | };
|
299 | }
|
300 |
|
301 | function generateBaseScale({
|
302 | colorKeys,
|
303 | colorspace = 'LAB',
|
304 | smooth
|
305 | } = {}) {
|
306 |
|
307 | let swatches = 1000;
|
308 | let scale = createScale({swatches: swatches, colorKeys: colorKeys, colorspace: colorspace, shift: 1, smooth: smooth});
|
309 | let newColors = scale.colorsHex;
|
310 |
|
311 | let colorObj = newColors
|
312 |
|
313 | .map((c, i) => { return { value: Math.round(cArray(c)[2]), index: i } });
|
314 |
|
315 | let filteredArr = removeDuplicates(colorObj, "value")
|
316 | .map(data => newColors[data.index]);
|
317 |
|
318 | return filteredArr;
|
319 | }
|
320 |
|
321 | function generateContrastColors({
|
322 | colorKeys,
|
323 | base,
|
324 | ratios,
|
325 | colorspace = 'LAB',
|
326 | smooth = false,
|
327 | output = 'HEX'
|
328 | } = {}) {
|
329 | if (!base) {
|
330 | throw new Error(`Base is undefined`);
|
331 | }
|
332 | if (!colorKeys) {
|
333 | throw new Error(`Color Keys are undefined`);
|
334 | }
|
335 | for (let i=0; i<colorKeys.length; i++) {
|
336 | if (colorKeys[i].length < 6) {
|
337 | throw new Error('Color Key must be greater than 6 and include hash # if hex.');
|
338 | }
|
339 | else if (colorKeys[i].length == 6 && colorKeys[i].charAt(0) != 0) {
|
340 | throw new Error('Color Key missing hash #');
|
341 | }
|
342 | }
|
343 | if (!ratios) {
|
344 | throw new Error(`Ratios are undefined`);
|
345 | }
|
346 | const outputFormat = colorSpaces[output];
|
347 | if (!outputFormat) {
|
348 | throw new Error(`Colorspace “${output}” not supported`);
|
349 | }
|
350 |
|
351 | let swatches = 3000;
|
352 |
|
353 | let scaleData = createScale({swatches: swatches, colorKeys: colorKeys, colorspace: colorspace, shift: 1, smooth: smooth});
|
354 | let baseV = (d3.hsluv(base).v) / 100;
|
355 |
|
356 | let Contrasts = d3.range(swatches).map((d) => {
|
357 | let rgbArray = [d3.rgb(scaleData.scale(d)).r, d3.rgb(scaleData.scale(d)).g, d3.rgb(scaleData.scale(d)).b];
|
358 | let baseRgbArray = [d3.rgb(base).r, d3.rgb(base).g, d3.rgb(base).b];
|
359 | let ca = contrast(rgbArray, baseRgbArray, baseV).toFixed(2);
|
360 |
|
361 | return Number(ca);
|
362 | });
|
363 |
|
364 | let contrasts = Contrasts.filter(el => el != null);
|
365 |
|
366 | let newColors = [];
|
367 | ratios = ratios.map(Number);
|
368 |
|
369 |
|
370 | for (let i=0; i < ratios.length; i++){
|
371 | let r = binarySearch(contrasts, ratios[i], baseV);
|
372 |
|
373 |
|
374 |
|
375 | newColors.push(fixColorValue(scaleData.colors[r], output));
|
376 |
|
377 | }
|
378 |
|
379 | return newColors;
|
380 | }
|
381 |
|
382 |
|
383 | function filterNaN(x) {
|
384 | if(isNaN(x)) {
|
385 | return 0;
|
386 | } else {
|
387 | return x;
|
388 | }
|
389 | }
|
390 |
|
391 |
|
392 | function fixColorValue(color, format, object = false) {
|
393 | let colorObj = colorSpaces[format].function(color);
|
394 | let propArray = colorSpaces[format].channels;
|
395 |
|
396 | let newColorObj = {
|
397 | [propArray[0]]: filterNaN(colorObj[propArray[0]]),
|
398 | [propArray[1]]: filterNaN(colorObj[propArray[1]]),
|
399 | [propArray[2]]: filterNaN(colorObj[propArray[2]])
|
400 | }
|
401 |
|
402 |
|
403 | if (format === "HSLuv") {
|
404 | for (let i = 0; i < propArray.length; i++) {
|
405 |
|
406 | let roundedPct = Math.round(newColorObj[propArray[i]]);
|
407 | newColorObj[propArray[i]] = roundedPct;
|
408 | }
|
409 | }
|
410 |
|
411 | else if (format === "LAB" || format === "LCH" || format === "CAM02" || format === "CAM02p") {
|
412 | for (let i = 0; i < propArray.length; i++) {
|
413 | let roundedPct = Math.round(newColorObj[propArray[i]]);
|
414 |
|
415 | if (propArray[i] === "h" && !object) {
|
416 | roundedPct = roundedPct + "deg";
|
417 | }
|
418 | if (propArray[i] === "l" && !object || propArray[i] === "J" && !object) {
|
419 | roundedPct = roundedPct + "%";
|
420 | }
|
421 |
|
422 | newColorObj[propArray[i]] = roundedPct;
|
423 |
|
424 | }
|
425 | }
|
426 | else {
|
427 | for (let i = 0; i < propArray.length; i++) {
|
428 | if (propArray[i] === "s" || propArray[i] === "l" || propArray[i] === "v") {
|
429 |
|
430 | let roundedPct = parseFloat(newColorObj[propArray[i]].toFixed(2));
|
431 | if(object) {
|
432 | newColorObj[propArray[i]] = roundedPct;
|
433 | }
|
434 | else {
|
435 | newColorObj[propArray[i]] = Math.round(roundedPct * 100) + "%";
|
436 | }
|
437 | }
|
438 | else {
|
439 | let roundedPct = parseFloat(newColorObj[propArray[i]].toFixed());
|
440 | if (propArray[i] === "h" && !object) {
|
441 | roundedPct = roundedPct + "deg";
|
442 | }
|
443 | newColorObj[propArray[i]] = roundedPct;
|
444 | }
|
445 | }
|
446 | }
|
447 |
|
448 | let stringName = colorSpaces[format].name;
|
449 | let stringValue;
|
450 |
|
451 | if (format === "HEX") {
|
452 | stringValue = d3.rgb(color).formatHex();
|
453 | } else {
|
454 | let str0, srt1, str2;
|
455 | if (format === "LCH") {
|
456 |
|
457 |
|
458 |
|
459 | str0 = newColorObj[propArray[2]] + ", ";
|
460 | str1 = newColorObj[propArray[1]] + ", ";
|
461 | str2 = newColorObj[propArray[0]];
|
462 | }
|
463 | else {
|
464 | str0 = newColorObj[propArray[0]] + ", ";
|
465 | str1 = newColorObj[propArray[1]] + ", ";
|
466 | str2 = newColorObj[propArray[2]];
|
467 | }
|
468 |
|
469 | stringValue = stringName + "(" + str0 + str1 + str2 + ")";
|
470 | }
|
471 |
|
472 | if (object) {
|
473 |
|
474 | return newColorObj;
|
475 | } else {
|
476 | return stringValue;
|
477 | }
|
478 | }
|
479 |
|
480 | function luminance(r, g, b) {
|
481 | let a = [r, g, b].map((v) => {
|
482 | v /= 255;
|
483 | return v <= 0.03928
|
484 | ? v / 12.92
|
485 | : Math.pow( (v + 0.055) / 1.055, 2.4 );
|
486 | });
|
487 | return (a[0] * 0.2126) + (a[1] * 0.7152) + (a[2] * 0.0722);
|
488 | }
|
489 |
|
490 | function contrast(color, base, baseV) {
|
491 | let colorLum = luminance(color[0], color[1], color[2]);
|
492 | let baseLum = luminance(base[0], base[1], base[2]);
|
493 |
|
494 | let cr1 = (colorLum + 0.05) / (baseLum + 0.05);
|
495 | let cr2 = (baseLum + 0.05) / (colorLum + 0.05);
|
496 |
|
497 | if (baseV < 0.5) {
|
498 | if (cr1 >= 1) {
|
499 | return cr1;
|
500 | }
|
501 | else {
|
502 | return cr2 * -1;
|
503 | }
|
504 | }
|
505 | else {
|
506 | if (cr1 < 1) {
|
507 | return cr2;
|
508 | }
|
509 | else {
|
510 | return cr1 * -1;
|
511 | }
|
512 | }
|
513 | }
|
514 |
|
515 | function minPositive(r) {
|
516 | if (!r) { throw new Error('Array undefined');}
|
517 | if (!Array.isArray(r)) { throw new Error('Passed object is not an array');}
|
518 | let arr = [];
|
519 |
|
520 | for(let i=0; i < r.length; i++) {
|
521 | if(r[i] >= 1) {
|
522 | arr.push(r[i]);
|
523 | }
|
524 | }
|
525 | return Math.min(...arr);
|
526 | }
|
527 |
|
528 | function ratioName(r) {
|
529 | if (!r) { throw new Error('Ratios undefined');}
|
530 | r = r.sort(function(a, b){return a - b});
|
531 |
|
532 | let min = minPositive(r);
|
533 | let minIndex = r.indexOf(min);
|
534 | let nArr = [];
|
535 |
|
536 | let rNeg = r.slice(0, minIndex);
|
537 | let rPos = r.slice(minIndex, r.length);
|
538 |
|
539 |
|
540 | for (let i=0; i < rNeg.length; i++) {
|
541 | let d = 1/(rNeg.length + 1);
|
542 | let m = d * 100;
|
543 | let nVal = m * (i + 1);
|
544 | nArr.push(Number(nVal.toFixed()));
|
545 | }
|
546 |
|
547 | for (let i=0; i < rPos.length; i++) {
|
548 | nArr.push((i+1)*100);
|
549 | }
|
550 | nArr.sort(function(a, b){return a - b});
|
551 |
|
552 | return nArr;
|
553 | }
|
554 |
|
555 | function generateAdaptiveTheme({
|
556 | colorScales,
|
557 | baseScale,
|
558 | brightness,
|
559 | contrast = 1,
|
560 | output = 'HEX'
|
561 | }) {
|
562 | if (!baseScale) {
|
563 | throw new Error('baseScale is undefined');
|
564 | }
|
565 | let found = false;
|
566 | for(let i = 0; i < colorScales.length; i++) {
|
567 | if (colorScales[i].name !== baseScale) {
|
568 | found = true;
|
569 | }
|
570 | }
|
571 | if (found = false) {
|
572 | throw new Error('baseScale must match the name of a colorScales object');
|
573 | }
|
574 |
|
575 | if (!colorScales) {
|
576 | throw new Error('colorScales are undefined');
|
577 | }
|
578 | if (!Array.isArray(colorScales)) {
|
579 | throw new Error('colorScales must be an array of objects');
|
580 | }
|
581 | for (let i=0; i < colorScales.length; i ++) {
|
582 |
|
583 |
|
584 |
|
585 |
|
586 |
|
587 |
|
588 |
|
589 |
|
590 | }
|
591 |
|
592 | if (brightness === undefined) {
|
593 | return function(brightness, contrast) {
|
594 | return generateAdaptiveTheme({baseScale: baseScale, colorScales: colorScales, brightness: brightness, contrast: contrast, output: output});
|
595 | }
|
596 | }
|
597 | else {
|
598 |
|
599 | let baseIndex = colorScales.findIndex( x => x.name === baseScale );
|
600 | let baseKeys = colorScales[baseIndex].colorKeys;
|
601 | let baseMode = colorScales[baseIndex].colorspace;
|
602 | let smooth = colorScales[baseIndex].smooth;
|
603 |
|
604 |
|
605 | let bscale = generateBaseScale({colorKeys: baseKeys, colorspace: baseMode, smooth: smooth});
|
606 | let bval = bscale[brightness];
|
607 | let baseObj = {
|
608 | background: bval
|
609 | };
|
610 |
|
611 | let arr = [];
|
612 | arr.push(baseObj);
|
613 |
|
614 | for (let i = 0; i < colorScales.length; i++) {
|
615 | if (!colorScales[i].name) {
|
616 | throw new Error('Color missing name');
|
617 | }
|
618 | let name = colorScales[i].name;
|
619 |
|
620 | let ratioInput = colorScales[i].ratios;
|
621 | let ratios;
|
622 | let swatchNames;
|
623 |
|
624 | if(Array.isArray(ratioInput)) {
|
625 | ratios = ratioInput;
|
626 | } else {
|
627 | ratios = Object.values(ratioInput);
|
628 | swatchNames = Object.keys(ratioInput);
|
629 | }
|
630 |
|
631 | let smooth = colorScales[i].smooth;
|
632 | let newArr = [];
|
633 | let colorObj = {
|
634 | name: name,
|
635 | values: newArr
|
636 | };
|
637 |
|
638 | ratios = ratios.map(function(d) {
|
639 | let r;
|
640 | if(d > 1) {
|
641 | r = ((d-1) * contrast) + 1;
|
642 | }
|
643 | else if(d < -1) {
|
644 | r = ((d+1) * contrast) - 1;
|
645 | }
|
646 | else {
|
647 | r = 1;
|
648 | }
|
649 | return Number(r.toFixed(2));
|
650 | });
|
651 |
|
652 | let outputColors = generateContrastColors({
|
653 | colorKeys: colorScales[i].colorKeys,
|
654 | colorspace: colorScales[i].colorspace,
|
655 | ratios: ratios,
|
656 | base: bval,
|
657 | smooth: smooth,
|
658 | output: output
|
659 | });
|
660 |
|
661 | for (let i=0; i < outputColors.length; i++) {
|
662 | let n;
|
663 | if(!swatchNames) {
|
664 | let rVal = ratioName(ratios)[i];
|
665 | n = name.concat(rVal);
|
666 | }
|
667 | else {
|
668 | n = swatchNames[i];
|
669 | }
|
670 |
|
671 | let obj = {
|
672 | name: n,
|
673 | contrast: ratios[i],
|
674 | value: outputColors[i]
|
675 | };
|
676 | newArr.push(obj)
|
677 | }
|
678 | arr.push(colorObj);
|
679 |
|
680 | }
|
681 |
|
682 | return arr;
|
683 | }
|
684 | }
|
685 |
|
686 |
|
687 |
|
688 | function binarySearch(list, value, baseLum) {
|
689 |
|
690 | let start = 0
|
691 | let stop = list.length - 1
|
692 | let middle = Math.floor((start + stop) / 2)
|
693 |
|
694 | let minContrast = Math.min(...list);
|
695 | let maxContrast = Math.max(...list);
|
696 |
|
697 |
|
698 | while (list[middle] !== value && start < stop) {
|
699 |
|
700 | if (baseLum > 0.5) {
|
701 | if (value < list[middle]) {
|
702 | stop = middle - 1
|
703 | }
|
704 | else {
|
705 | start = middle + 1
|
706 | }
|
707 | }
|
708 | else {
|
709 | if (value > list[middle]) {
|
710 | stop = middle - 1
|
711 | }
|
712 | else {
|
713 | start = middle + 1
|
714 | }
|
715 | }
|
716 |
|
717 | middle = Math.floor((start + stop) / 2)
|
718 | }
|
719 |
|
720 |
|
721 | let closest = list.reduce((prev, curr) => curr > value ? curr : prev);
|
722 |
|
723 |
|
724 | return (list[middle] == !value) ? closest : middle
|
725 | }
|
726 |
|
727 | module.exports = {
|
728 | createScale,
|
729 | luminance,
|
730 | contrast,
|
731 | binarySearch,
|
732 | generateBaseScale,
|
733 | generateContrastColors,
|
734 | minPositive,
|
735 | ratioName,
|
736 | generateAdaptiveTheme,
|
737 | fixColorValue
|
738 | };
|