coloration.ts 17.7 KB
Newer Older
Zéfling's avatar
Zéfling committed
1
2
3
// tslint:disable:no-bitwise

const pattern = {
Zéfling's avatar
Zéfling committed
4
    hexa: /^#?(([\da-f]{3})(([\da-f]{3})([\da-f]{2})?|[\da-f]{1})?)$/i,
Zéfling's avatar
Zéfling committed
5
    rgba: /^rgba?\(([\d]*(\.[\d]+)?)(,?\s*|\s+)([\d]*(\.[\d]+)?)(,?\s*|\s+)([\d]*(\.[\d]+)?)((,\s*)([\d]*(\.[\d]+)?))?\)$/i,
Zéfling's avatar
Zéfling committed
6
    hsva: /^hsla?\(([\d]*(\.[\d]+)?)(,?\s*|\s+)([\d]*(\.[\d]+)?)\%(,?\s*|\s+)([\d]*(\.[\d]+)?)\%((,\s*)([\d]*(\.[\d]+)?))?\)$/i,
Zéfling's avatar
Zéfling committed
7
8
};

9
export interface RGB {
Zéfling's avatar
Zéfling committed
10
    /** Red color value [0,255]   */ r: number;
11
    /** Green color value [0,255] */ g: number;
Zéfling's avatar
Zéfling committed
12
13
    /** Blue color value [0,255]  */ b: number;
    /** Alpha color value [0,1]   */ a?: number;
14
15
}
export interface HSV {
Zéfling's avatar
Zéfling committed
16
17
18
19
    /** Hue color value [0,360]             */ h: number;
    /** Saluration color value [0,100]      */ s: number;
    /** Value/lightness color value [0,100] */ v: number;
    /** Alpha color value [0,1]             */ a?: number;
20
}
21
export interface ColorData {
22
    // RGB
Zéfling's avatar
Zéfling committed
23
24
25
    /** Red color value [-256,255]       */ r?: number;
    /** Green color value [-256,255]     */ g?: number;
    /** Blue color value [-256,255]      */ b?: number;
26
    // HSV/HSL
Zéfling's avatar
Zéfling committed
27
28
29
    /** Hue value [-360,360]             */ h?: number;
    /** Saluration value [-100,100]      */ s?: number;
    /** Value/lightness value [-100,100] */ v?: number;
30
    // Other
Zéfling's avatar
Zéfling committed
31
32
33
34
35
36
37
38
    /** luminosity value [-1,1]          */ luminosity?: number;
    /** color mask                       */ maskColor?: string;
    /** color mask opacity [0,1]         */ maskOpacity?: number;
    // alpha
    /** color mask alpha [-1,1]          */ alpha?: number;
}

export interface ColorNumber {
Zéfling's avatar
Zéfling committed
39
40
    /** intcolor */ intColor: number;
    /** Alpha color value [0,1] */ alpha: number;
41
42
}

Zéfling's avatar
Zéfling committed
43
export class Coloration {
44
45
46
    /**
     * CSS color list
     */
Zéfling's avatar
Zéfling committed
47
48
    static colorsName: any = {
        // CSS 1
Zéfling's avatar
Zéfling committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
        black: '#000000',
        silver: '#c0c0c0',
        gray: '#808080',
        white: '#ffffff',
        maroon: '#800000',
        red: '#ff0000',
        purple: '#800080',
        fuchsia: '#ff00ff',
        green: '#008000',
        lime: '#00ff00',
        olive: '#808000',
        yellow: '#ffff00',
        navy: '#000080',
        blue: '#0000ff',
        teal: '#008080',
        aqua: '#00ffff',
Zéfling's avatar
Zéfling committed
65
66
67
        // CSS 2 (Revision 1)
        orange: '#ffa500',
        // CSS 3
Zéfling's avatar
Zéfling committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
        aliceblue: '#f0f8ff',
        antiquewhite: '#faebd7',
        aquamarine: '#7fffd4',
        azure: '#f0ffff',
        beige: '#f5f5dc',
        bisque: '#ffe4c4',
        blanchedalmond: '#ffebcd',
        blueviolet: '#8a2be2',
        brown: '#a52a2a',
        burlywood: '#deb887',
        cadetblue: '#5f9ea0',
        chartreuse: '#7fff00',
        chocolate: '#d2691e',
        coral: '#ff7f50',
        cornflowerblue: '#6495ed',
        cornsilk: '#fff8dc',
        crimson: '#dc143c',
        cyan: '#00ffff',
        deaqua: '#00ffff',
        darkblue: '#00008b',
        darkcyan: '#008b8b',
        darkgoldenrod: '#b8860b',
        darkgray: '#a9a9a9',
        darkgreen: '#006400',
        darkgrey: '#a9a9a9',
        darkkhaki: '#bdb76b',
        darkmagenta: '#8b008b',
        darkolivegreen: '#556b2f',
        darkorange: '#ff8c00',
        darkorchid: '#9932cc',
        darkred: '#8b0000',
        darksalmon: '#e9967a',
        darkseagreen: '#8fbc8f',
        darkslateblue: '#483d8b',
        darkslategray: '#2f4f4f',
        darkslategrey: '#2f4f4f',
        darkturquoise: '#00ced1',
        darkviolet: '#9400d3',
        deeppink: '#ff1493',
        deepskyblue: '#00bfff',
        dimgray: '#696969',
        dimgrey: '#696969',
        dodgerblue: '#1e90ff',
        firebrick: '#b22222',
        floralwhite: '#fffaf0',
        forestgreen: '#228b22',
        gainsboro: '#dcdcdc',
        ghostwhite: '#f8f8ff',
        gold: '#ffd700',
        goldenrod: '#daa520',
        greenyellow: '#adff2f',
        grey: '#808080',
        honeydew: '#f0fff0',
        hotpink: '#ff69b4',
        indianred: '#cd5c5c',
        indigo: '#4b0082',
        ivory: '#fffff0',
        khaki: '#f0e68c',
        lavender: '#e6e6fa',
        lavenderblush: '#fff0f5',
        lawngreen: '#7cfc00',
        lemonchiffon: '#fffacd',
        lightblue: '#add8e6',
        lightcoral: '#f08080',
        lightcyan: '#e0ffff',
        lightgoldenrodyellow: '#fafad2',
        lightgray: '#d3d3d3',
        lightgreen: '#90ee90',
        lightgrey: '#d3d3d3',
        lightpink: '#ffb6c1',
        lightsalmon: '#ffa07a',
        lightseagreen: '#20b2aa',
        lightskyblue: '#87cefa',
        lightslategray: '#778899',
        lightslategrey: '#778899',
        lightsteelblue: '#b0c4de',
        lightyellow: '#ffffe0',
        limegreen: '#32cd32',
        linen: '#faf0e6',
        magenta: '#ff00ff',
        defuchsia: '#ff00ff',
        mediumaquamarine: '#66cdaa',
        mediumblue: '#0000cd',
        mediumorchid: '#ba55d3',
        mediumpurple: '#9370db',
        mediumseagreen: '#3cb371',
        mediumslateblue: '#7b68ee',
        mediumspringgreen: '#00fa9a',
        mediumturquoise: '#48d1cc',
        mediumvioletred: '#c71585',
        midnightblue: '#191970',
        mintcream: '#f5fffa',
        mistyrose: '#ffe4e1',
        moccasin: '#ffe4b5',
        navajowhite: '#ffdead',
        oldlace: '#fdf5e6',
        olivedrab: '#6b8e23',
        orangered: '#ff4500',
        orchid: '#da70d6',
        palegoldenrod: '#eee8aa',
        palegreen: '#98fb98',
        paleturquoise: '#afeeee',
        palevioletred: '#db7093',
        papayawhip: '#ffefd5',
        peachpuff: '#ffdab9',
        peru: '#cd853f',
        pink: '#ffc0cb',
        plum: '#dda0dd',
        powderblue: '#b0e0e6',
        rosybrown: '#bc8f8f',
        royalblue: '#4169e1',
        saddlebrown: '#8b4513',
        salmon: '#fa8072',
        sandybrown: '#f4a460',
        seagreen: '#2e8b57',
        seashell: '#fff5ee',
        sienna: '#a0522d',
        skyblue: '#87ceeb',
        slateblue: '#6a5acd',
        slategray: '#708090',
        slategrey: '#708090',
        snow: '#fffafa',
        springgreen: '#00ff7f',
        steelblue: '#4682b4',
        tan: '#d2b48c',
        thistle: '#d8bfd8',
        tomato: '#ff6347',
        turquoise: '#40e0d0',
        violet: '#ee82ee',
        wheat: '#f5deb3',
        whitesmoke: '#f5f5f5',
        yellowgreen: '#9acd32',
Zéfling's avatar
Zéfling committed
200
        // CSS 4
Zéfling's avatar
Zéfling committed
201
        rebeccapurple: '#663399',
Zéfling's avatar
Zéfling committed
202
203
    };

Zéfling's avatar
Zéfling committed
204
205
206
    private calcColor: ColorNumber;
    private rgb: RGB = { r: 0, g: 0, b: 0, a: 1 };
    private hsv: HSV = { h: 0, s: 0, v: 0, a: 1 };
Zéfling's avatar
Zéfling committed
207
208

    constructor(public color: string) {
Zéfling's avatar
Zéfling committed
209
210
211
212
213
214
215
        this.reset();
    }

    /**
     * reinit to base color
     */
    reset() {
Zéfling's avatar
Zéfling committed
216
217
218
219
        if (this.color) {
            this.calcColor = this.parseColor(this.color);
            this.updateColor();
        }
Zéfling's avatar
Zéfling committed
220
    }
Zéfling's avatar
Zéfling committed
221
222
223
224

    /**
     * change the luminosity of a color
     * @param lum value between -1 and 1
225
     * @returns Coloration
Zéfling's avatar
Zéfling committed
226
     */
227
228
229
230
    changeLuminosity(lum: number): Coloration {
        lum = this.minmax(lum, -1, 1);
        this.maskColor(lum < 0 ? '#000' : '#FFF', Math.abs(lum));
        return this;
Zéfling's avatar
Zéfling committed
231
232
233
234
235
236
    }

    /**
     * add color with a mark
     * @param color additional color
     * @param opacity value of opacity between 0 and 1 for the additional color
237
     * @returns Coloration
Zéfling's avatar
Zéfling committed
238
     */
239
    maskColor(color: string, opacity: number = 1): Coloration {
Zéfling's avatar
Zéfling committed
240
241
242
        if (this.calcColor) {
            const baseColor = this.calcColor;
            const additionalColor = this.parseColor(color);
Zéfling's avatar
Zéfling committed
243

Zéfling's avatar
Zéfling committed
244
            const lum = this.minmax(opacity, 0, 1);
Zéfling's avatar
Zéfling committed
245

Zéfling's avatar
Zéfling committed
246
            const R = baseColor.intColor >> 16;
Zéfling's avatar
Zéfling committed
247
248
            const G = (baseColor.intColor >> 8) & 0x00ff;
            const B = baseColor.intColor & 0x0000ff;
249

Zéfling's avatar
Zéfling committed
250
            if (additionalColor.alpha) {
251
                this.calcColor.alpha = this.minmax(baseColor.alpha + additionalColor.alpha * opacity, 0, 1);
Zéfling's avatar
Zéfling committed
252
253
254
            }
            this.calcColor.intColor = this.rgbToInt(
                Math.round(((additionalColor.intColor >> 16) - R) * lum) + R,
Zéfling's avatar
Zéfling committed
255
256
                Math.round((((additionalColor.intColor >> 8) & 0x00ff) - G) * lum) + G,
                Math.round(((additionalColor.intColor & 0x0000ff) - B) * lum) + B,
Zéfling's avatar
Zéfling committed
257
258
            );
            this.updateColor();
Zéfling's avatar
Zéfling committed
259
        }
260
261
262
263
264
265
        return this;
    }

    /**
     * change color with color parameters
     * @param colorData additionnal parameters
266
     * @returns Coloration
267
268
     */
    addColor(colorData: ColorData) {
Zéfling's avatar
Zéfling committed
269
270
271
        if (this.calcColor) {
            if (colorData.luminosity) {
                this.changeLuminosity(colorData.luminosity);
272
273
            }

Zéfling's avatar
Zéfling committed
274
275
            if (colorData.maskColor) {
                this.maskColor(colorData.maskColor, this.minmax(colorData.maskOpacity || 0, 0, 1));
276
            }
Zéfling's avatar
Zéfling committed
277
278
279

            if (colorData.r || colorData.g || colorData.b) {
                if (colorData.r) {
Zéfling's avatar
Zéfling committed
280
                    this.rgb.r = this.minmax(this.rgb.r + +colorData.r, 0, 255);
Zéfling's avatar
Zéfling committed
281
282
                }
                if (colorData.g) {
Zéfling's avatar
Zéfling committed
283
                    this.rgb.g = this.minmax(this.rgb.g + +colorData.g, 0, 255);
Zéfling's avatar
Zéfling committed
284
285
                }
                if (colorData.b) {
Zéfling's avatar
Zéfling committed
286
                    this.rgb.b = this.minmax(this.rgb.b + +colorData.b, 0, 255);
Zéfling's avatar
Zéfling committed
287
288
289
                }
                this.calcColor.intColor = this.rgbToInt(this.rgb.r, this.rgb.g, this.rgb.b);
                this.updateColor();
290
            }
Zéfling's avatar
Zéfling committed
291
292
293

            if (colorData.h || colorData.s || colorData.v) {
                if (colorData.h) {
Zéfling's avatar
Zéfling committed
294
                    this.hsv.h = (this.hsv.h + +colorData.h + 360) % 360;
Zéfling's avatar
Zéfling committed
295
296
                }
                if (colorData.s) {
Zéfling's avatar
Zéfling committed
297
                    this.hsv.s = this.minmax(this.hsv.s + +colorData.s, 0, 100);
Zéfling's avatar
Zéfling committed
298
299
                }
                if (colorData.v) {
Zéfling's avatar
Zéfling committed
300
                    this.hsv.v = this.minmax(this.hsv.v + +colorData.v, 0, 100);
Zéfling's avatar
Zéfling committed
301
302
303
                }
                this.calcColor.intColor = this.hsvToInt(this.hsv.h, this.hsv.s, this.hsv.v);
                this.updateColor();
304
305
            }

Zéfling's avatar
Zéfling committed
306
            if (colorData.alpha) {
Zéfling's avatar
Zéfling committed
307
308
309
310
                this.hsv.a =
                    this.rgb.a =
                    this.calcColor.alpha =
                        this.minmax(this.calcColor.alpha + +colorData.alpha, 0, 1);
Zéfling's avatar
Zéfling committed
311
            }
Zéfling's avatar
Zéfling committed
312
        }
313
314
315
        return this;
    }

316
317
318
319
320
    /**
     * RGB informations
     * @returns RGB
     */
    getRGB(): RGB {
Zéfling's avatar
Zéfling committed
321
        return this.calcColor ? this.value<RGB>(() => Object.assign({}, this.rgb)) : null;
322
323
324
325
326
327
328
    }

    /**
     * HSV informations
     * @returns HSV
     */
    getHSV(): HSV {
Zéfling's avatar
Zéfling committed
329
        return this.calcColor ? this.value<HSV>(() => Object.assign({}, this.hsv)) : null;
330
331
    }

332
333
    /**
     * color in #HEX format
334
     * @returns string of #HEX
335
336
     */
    toHEX(): string {
Zéfling's avatar
Zéfling committed
337
338
339
340
341
342
343
344
345
346
        return this.calcColor
            ? this.value<string>(
                  () =>
                      '#' +
                      (0x1000000 + this.calcColor.intColor).toString(16).slice(1) +
                      (this.calcColor.alpha < 1
                          ? (0x100 + Math.round(this.calcColor.alpha * 255)).toString(16).slice(1)
                          : ''),
              )
            : null;
347
348
349
350
    }

    /**
     * color in HVL() format
351
     * @returns string of HVL(H, S%, V%)
352
353
     */
    toHSL(): string {
Zéfling's avatar
Zéfling committed
354
355
356
357
358
        return this.calcColor
            ? this.value<string>(
                  () => `hsl(${this.hsv.h}, ${this.hsv.s}%, ${this.hsv.v}%${this.hsv.a < 1 ? ', ' + this.hsv.a : ''})`,
              )
            : null;
359
360
361
362
    }

    /**
     * color in RGB() format
363
     * @returns string of RGB(R, G, B)
364
365
     */
    toRGB(): string {
Zéfling's avatar
Zéfling committed
366
367
368
369
370
        return this.calcColor
            ? this.value<string>(
                  () => `rgb(${this.rgb.r}, ${this.rgb.g}, ${this.rgb.b}${this.rgb.a < 1 ? ', ' + this.rgb.a : ''})`,
              )
            : null;
371
372
    }

373
374
375
    /**
     * update colors data : RGB and HSL
     */
376
    private updateColor() {
Zéfling's avatar
Zéfling committed
377
        const color = this.calcColor.intColor;
378
        // update RGB
Zéfling's avatar
Zéfling committed
379
        this.rgb.r = color >> 16;
Zéfling's avatar
Zéfling committed
380
381
        this.rgb.g = (color >> 8) & 0x00ff;
        this.rgb.b = color & 0x0000ff;
Zéfling's avatar
Zéfling committed
382
        this.rgb.a = this.calcColor.alpha;
383
384
385

        // update HSL
        this.hsv = this.rgb2hsv(this.rgb.r, this.rgb.g, this.rgb.b);
Zéfling's avatar
Zéfling committed
386
        this.hsv.a = this.calcColor.alpha;
Zéfling's avatar
Zéfling committed
387
388
389
390
391
392
393
    }

    /**
     * change color
     * @param color parse color : name, #HEXA, rgba()
     * @returns int color
     */
Zéfling's avatar
Zéfling committed
394
    private parseColor(color: string): ColorNumber {
Zéfling's avatar
Zéfling committed
395
396
397
398
399
400
401
        // si named color
        if (Coloration.colorsName[color]) {
            color = Coloration.colorsName[color];
        }

        // validate hexa string #RGB, #RGBA, #RRGGBB, #RRGGBBAA (ignore alpha)
        const matchHex = String(color).match(pattern.hexa);
Zéfling's avatar
Zéfling committed
402
403
        let intColor: number;
        let alpha = 1;
Zéfling's avatar
Zéfling committed
404
        if (matchHex) {
Zéfling's avatar
Zéfling committed
405
            const hexaColor = matchHex[4]
Zéfling's avatar
Zéfling committed
406
407
                ? matchHex[2] + matchHex[4]
                : matchHex[2][0] + matchHex[2][0] + matchHex[2][1] + matchHex[2][1] + matchHex[2][2] + matchHex[2][2];
Zéfling's avatar
Zéfling committed
408
            const hexaAlpha = matchHex[4] ? matchHex[5] : matchHex[3] ? matchHex[3][1] + matchHex[3][1] : undefined;
Zéfling's avatar
Zéfling committed
409
            intColor = parseInt(hexaColor, 16);
Zéfling's avatar
Zéfling committed
410
            alpha = hexaAlpha !== undefined ? parseInt(hexaAlpha, 16) / 255 : 1;
Zéfling's avatar
Zéfling committed
411
412
413
414
415
416
        }

        if (intColor === undefined) {
            // validate rgb() / rgba()
            const matchRgb = String(color).match(pattern.rgba);
            if (matchRgb) {
Zéfling's avatar
Zéfling committed
417
418
419
420
421
                intColor = this.rgbToInt(
                    parseInt(matchRgb[1], 10),
                    parseInt(matchRgb[4], 10),
                    parseInt(matchRgb[7], 10),
                );
Zéfling's avatar
Zéfling committed
422
                alpha = matchRgb[11] !== undefined ? parseFloat(matchRgb[11]) : 1;
Zéfling's avatar
Zéfling committed
423
424
425
426
427
428
429
            }
        }

        if (intColor === undefined) {
            // validate hsv() / hsva()
            const matchHsv = String(color).match(pattern.hsva);
            if (matchHsv) {
Zéfling's avatar
Zéfling committed
430
431
432
433
434
                intColor = this.hsvToInt(
                    parseInt(matchHsv[1], 10),
                    parseInt(matchHsv[4], 10),
                    parseInt(matchHsv[7], 10),
                );
Zéfling's avatar
Zéfling committed
435
                alpha = matchHsv[11] !== undefined ? parseFloat(matchHsv[11]) : 1;
Zéfling's avatar
Zéfling committed
436
437
438
            }
        }

Zéfling's avatar
Zéfling committed
439
        return { intColor, alpha };
Zéfling's avatar
Zéfling committed
440
441
    }

Zéfling's avatar
Zéfling committed
442
443
444
445
446
447
448
449
    /**
     * convert HSV/HSL to int RGB
     * @param hue Hue [0, 360]
     * @param saturation Saturation [0, 100]
     * @param value Value [0, 100]
     * @returns int RGB
     * @see https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
     */
450
    private hsvToInt(hue: number, saturation: number, value: number): number {
Zéfling's avatar
Zéfling committed
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
        saturation = Math.max(0, saturation);

        const s = saturation / 100;
        const v = value / 100;
        const h = hue / 360;

        if (saturation === 0) {
            return this.rgbToInt(v * 256, v * 256, v * 256);
        }

        const q = v < 0.5 ? v * (1 + s) : v + s - v * s;
        const p = 2 * v - q;
        const r = this.hue2rgb(p, q, h + 1 / 3);
        const g = this.hue2rgb(p, q, h);
        const b = this.hue2rgb(p, q, h - 1 / 3);

        return this.rgbToInt(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255));
    }

470
471
472
473
474
475
476
    /**
     * hue color calculation
     * @param p number
     * @param q number
     * @param t number
     * @returns int color [0, 255]
     */
Zéfling's avatar
Zéfling committed
477
    private hue2rgb(p: number, q: number, t: number): number {
Zéfling's avatar
Zéfling committed
478
479
480
481
482
483
484
485
486
487
488
489
490
491
        if (t < 0) {
            t += 1;
        } else if (t > 1) {
            t -= 1;
        }
        if (t < 1 / 6) {
            return p + (q - p) * 6 * t;
        }
        if (t < 1 / 2) {
            return q;
        }
        if (t < 2 / 3) {
            return p + (q - p) * (2 / 3 - t) * 6;
        }
Zéfling's avatar
Zéfling committed
492
493
494
        return p;
    }

495
496
    /**
     * Convert RGB to HSV
Zéfling's avatar
Zéfling committed
497
498
499
     * @param r red [0, 255]
     * @param g green [0, 255]
     * @param b blue [0, 255]
500
     * @returns HSV data
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
     * @see https://stackoverflow.com/questions/39118528/rgb-to-hsl-conversion
     * @see https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation
     */
    private rgb2hsv(r: number, g: number, b: number): HSV {
        // convert r,g,b [0,255] range to [0,1]
        r = r / 255;
        g = g / 255;
        b = b / 255;

        // get the min and max of r,g,b
        const max = Math.max(r, g, b);
        const min = Math.min(r, g, b);

        // lightness is the average of the largest and smallest color components
        const val = (max + min) / 2;
        let hue: number;
        let sat: number;

Zéfling's avatar
Zéfling committed
519
520
        if (max === min) {
            // no saturation
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
            hue = 0;
            sat = 0;
        } else {
            const c = max - min; // chroma
            // saturation is simply the chroma scaled to fill
            // the interval [0, 1] for every combination of hue and lightness
            sat = c / (1 - Math.abs(2 * val - 1));
            switch (max) {
                case r:
                    hue = (g - b) / c + (g < b ? 6 : 0);
                    break;
                case g:
                    hue = (b - r) / c + 2;
                    break;
                case b:
                    hue = (r - g) / c + 4;
                    break;
            }
        }

        return {
Zéfling's avatar
Zéfling committed
542
            h: Math.round(hue * 60), // °
543
            s: Math.round(sat * 100), // %
Zéfling's avatar
Zéfling committed
544
            v: Math.round(val * 100), // %
545
546
547
        };
    }

548
549
550
551
552
553
554
    /**
     * convert RGB data to int value
     * @param r red [0, 255]
     * @param g green [0, 255]
     * @param b blue [0, 255]
     * @returns int value
     */
Zéfling's avatar
Zéfling committed
555
    private rgbToInt(r: number, g: number, b: number): number {
Zéfling's avatar
Zéfling committed
556
        return Math.round(r) * 0x10000 + Math.round(g) * 0x100 + Math.round(b);
Zéfling's avatar
Zéfling committed
557
558
    }

559
560
561
562
563
564
565
566
    /**
     * bound a value between two values
     * @param value value
     * @param min min value
     * @param max max value
     * @param defaultValue replace an invalid vvalue
     * @returns value between min and max
     */
567
568
    private minmax(value: number, min: number, max: number, defaultValue = 0) {
        return Math.min(Math.max(value || defaultValue, min), max);
Zéfling's avatar
Zéfling committed
569
570
    }

571
572
573
574
575
576
    /**
     * test is intColor is valid for return a vlaue
     * @param value callback of value
     * @returns value or null
     */
    private value<L>(value: () => L): L {
Zéfling's avatar
Zéfling committed
577
        return !isNaN(this.calcColor.intColor) ? value() : null;
578
    }
Zéfling's avatar
Zéfling committed
579
}