/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export const enum CharCode { Null = 0, /** * The `\b` character. */ Backspace = 8, /** * The `\t` character. */ Tab = 9, /** * The `\n` character. */ LineFeed = 10, /** * The `\r` character. */ CarriageReturn = 13, Space = 32, /** * The `!` character. */ ExclamationMark = 33, /** * The `"` character. */ DoubleQuote = 34, /** * The `#` character. */ Hash = 35, /** * The `$` character. */ DollarSign = 36, /** * The `%` character. */ PercentSign = 37, /** * The `&` character. */ Ampersand = 38, /** * The `'` character. */ SingleQuote = 39, /** * The `(` character. */ OpenParen = 40, /** * The `)` character. */ CloseParen = 41, /** * The `*` character. */ Asterisk = 42, /** * The `+` character. */ Plus = 43, /** * The `,` character. */ Comma = 44, /** * The `-` character. */ Dash = 45, /** * The `.` character. */ Period = 46, /** * The `/` character. */ Slash = 47, Digit0 = 48, Digit1 = 49, Digit2 = 50, Digit3 = 51, Digit4 = 52, Digit5 = 53, Digit6 = 54, Digit7 = 55, Digit8 = 56, Digit9 = 57, /** * The `:` character. */ Colon = 58, /** * The `;` character. */ Semicolon = 59, /** * The `<` character. */ LessThan = 60, /** * The `=` character. */ Equals = 61, /** * The `>` character. */ GreaterThan = 62, /** * The `?` character. */ QuestionMark = 63, /** * The `@` character. */ AtSign = 64, A = 65, B = 66, C = 67, D = 68, E = 69, F = 70, G = 71, H = 72, I = 73, J = 74, K = 75, L = 76, M = 77, N = 78, O = 79, P = 80, Q = 81, R = 82, S = 83, T = 84, U = 85, V = 86, W = 87, X = 88, Y = 89, Z = 90, /** * The `[` character. */ OpenSquareBracket = 91, /** * The `\` character. */ Backslash = 92, /** * The `]` character. */ CloseSquareBracket = 93, /** * The `^` character. */ Caret = 94, /** * The `_` character. */ Underline = 95, /** * The ``(`)`` character. */ BackTick = 96, a = 97, b = 98, c = 99, d = 100, e = 101, f = 102, g = 103, h = 104, i = 105, j = 106, k = 107, l = 108, m = 109, n = 110, o = 111, p = 112, q = 113, r = 114, s = 115, t = 116, u = 117, v = 118, w = 119, x = 120, y = 121, z = 122, /** * The `{` character. */ OpenCurlyBrace = 123, /** * The `|` character. */ Pipe = 124, /** * The `}` character. */ CloseCurlyBrace = 125, /** * The `~` character. */ Tilde = 126, U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde U_Combining_Macron = 0x0304, // U+0304 Combining Macron U_Combining_Overline = 0x0305, // U+0305 Combining Overline U_Combining_Breve = 0x0306, // U+0306 Combining Breve U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above U_Combining_Ring_Above = 0x030A, // U+030A Combining Ring Above U_Combining_Double_Acute_Accent = 0x030B, // U+030B Combining Double Acute Accent U_Combining_Caron = 0x030C, // U+030C Combining Caron U_Combining_Vertical_Line_Above = 0x030D, // U+030D Combining Vertical Line Above U_Combining_Double_Vertical_Line_Above = 0x030E, // U+030E Combining Double Vertical Line Above U_Combining_Double_Grave_Accent = 0x030F, // U+030F Combining Double Grave Accent U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below U_Combining_Left_Angle_Above = 0x031A, // U+031A Combining Left Angle Above U_Combining_Horn = 0x031B, // U+031B Combining Horn U_Combining_Left_Half_Ring_Below = 0x031C, // U+031C Combining Left Half Ring Below U_Combining_Up_Tack_Below = 0x031D, // U+031D Combining Up Tack Below U_Combining_Down_Tack_Below = 0x031E, // U+031E Combining Down Tack Below U_Combining_Plus_Sign_Below = 0x031F, // U+031F Combining Plus Sign Below U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below U_Combining_Bridge_Below = 0x032A, // U+032A Combining Bridge Below U_Combining_Inverted_Double_Arch_Below = 0x032B, // U+032B Combining Inverted Double Arch Below U_Combining_Caron_Below = 0x032C, // U+032C Combining Caron Below U_Combining_Circumflex_Accent_Below = 0x032D, // U+032D Combining Circumflex Accent Below U_Combining_Breve_Below = 0x032E, // U+032E Combining Breve Below U_Combining_Inverted_Breve_Below = 0x032F, // U+032F Combining Inverted Breve Below U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below U_Combining_Inverted_Bridge_Below = 0x033A, // U+033A Combining Inverted Bridge Below U_Combining_Square_Below = 0x033B, // U+033B Combining Square Below U_Combining_Seagull_Below = 0x033C, // U+033C Combining Seagull Below U_Combining_X_Above = 0x033D, // U+033D Combining X Above U_Combining_Vertical_Tilde = 0x033E, // U+033E Combining Vertical Tilde U_Combining_Double_Overline = 0x033F, // U+033F Combining Double Overline U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below U_Combining_Not_Tilde_Above = 0x034A, // U+034A Combining Not Tilde Above U_Combining_Homothetic_Above = 0x034B, // U+034B Combining Homothetic Above U_Combining_Almost_Equal_To_Above = 0x034C, // U+034C Combining Almost Equal To Above U_Combining_Left_Right_Arrow_Below = 0x034D, // U+034D Combining Left Right Arrow Below U_Combining_Upwards_Arrow_Below = 0x034E, // U+034E Combining Upwards Arrow Below U_Combining_Grapheme_Joiner = 0x034F, // U+034F Combining Grapheme Joiner U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata U_Combining_X_Below = 0x0353, // U+0353 Combining X Below U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below U_Combining_Double_Ring_Below = 0x035A, // U+035A Combining Double Ring Below U_Combining_Zigzag_Above = 0x035B, // U+035B Combining Zigzag Above U_Combining_Double_Breve_Below = 0x035C, // U+035C Combining Double Breve Below U_Combining_Double_Breve = 0x035D, // U+035D Combining Double Breve U_Combining_Double_Macron = 0x035E, // U+035E Combining Double Macron U_Combining_Double_Macron_Below = 0x035F, // U+035F Combining Double Macron Below U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D U_Combining_Latin_Small_Letter_H = 0x036A, // U+036A Combining Latin Small Letter H U_Combining_Latin_Small_Letter_M = 0x036B, // U+036B Combining Latin Small Letter M U_Combining_Latin_Small_Letter_R = 0x036C, // U+036C Combining Latin Small Letter R U_Combining_Latin_Small_Letter_T = 0x036D, // U+036D Combining Latin Small Letter T U_Combining_Latin_Small_Letter_V = 0x036E, // U+036E Combining Latin Small Letter V U_Combining_Latin_Small_Letter_X = 0x036F, // U+036F Combining Latin Small Letter X /** * Unicode Character 'LINE SEPARATOR' (U+2028) * http://www.fileformat.info/info/unicode/char/2028/index.htm */ LINE_SEPARATOR = 0x2028, /** * Unicode Character 'PARAGRAPH SEPARATOR' (U+2029) * http://www.fileformat.info/info/unicode/char/2029/index.htm */ PARAGRAPH_SEPARATOR = 0x2029, /** * Unicode Character 'NEXT LINE' (U+0085) * http://www.fileformat.info/info/unicode/char/0085/index.htm */ NEXT_LINE = 0x0085, // http://www.fileformat.info/info/unicode/category/Sk/list.htm U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT U_DIAERESIS = 0x00A8, // U+00A8 DIAERESIS U_MACRON = 0x00AF, // U+00AF MACRON U_ACUTE_ACCENT = 0x00B4, // U+00B4 ACUTE ACCENT U_CEDILLA = 0x00B8, // U+00B8 CEDILLA U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, // U+02C4 MODIFIER LETTER UP ARROWHEAD U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING U_MODIFIER_LETTER_UP_TACK = 0x02D4, // U+02D4 MODIFIER LETTER UP TACK U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, // U+02D5 MODIFIER LETTER DOWN TACK U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, // U+02D6 MODIFIER LETTER PLUS SIGN U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, // U+02D7 MODIFIER LETTER MINUS SIGN U_BREVE = 0x02D8, // U+02D8 BREVE U_DOT_ABOVE = 0x02D9, // U+02D9 DOT ABOVE U_RING_ABOVE = 0x02DA, // U+02DA RING ABOVE U_OGONEK = 0x02DB, // U+02DB OGONEK U_SMALL_TILDE = 0x02DC, // U+02DC SMALL TILDE U_DOUBLE_ACUTE_ACCENT = 0x02DD, // U+02DD DOUBLE ACUTE ACCENT U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, // U+02DE MODIFIER LETTER RHOTIC HOOK U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, // U+02DF MODIFIER LETTER CROSS ACCENT U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, // U+02E6 MODIFIER LETTER HIGH TONE BAR U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, // U+02E7 MODIFIER LETTER MID TONE BAR U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, // U+02E8 MODIFIER LETTER LOW TONE BAR U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, // U+02ED MODIFIER LETTER UNASPIRATED U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD U_MODIFIER_LETTER_LOW_RING = 0x02F3, // U+02F3 MODIFIER LETTER LOW RING U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, // U+02F7 MODIFIER LETTER LOW TILDE U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, // U+02F8 MODIFIER LETTER RAISED COLON U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, // U+02FA MODIFIER LETTER END HIGH TONE U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, // U+02FB MODIFIER LETTER BEGIN LOW TONE U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, // U+02FC MODIFIER LETTER END LOW TONE U_MODIFIER_LETTER_SHELF = 0x02FD, // U+02FD MODIFIER LETTER SHELF U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, // U+02FE MODIFIER LETTER OPEN SHELF U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, // U+02FF MODIFIER LETTER LOW LEFT ARROW U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS U_GREEK_KORONIS = 0x1FBD, // U+1FBD GREEK KORONIS U_GREEK_PSILI = 0x1FBF, // U+1FBF GREEK PSILI U_GREEK_PERISPOMENI = 0x1FC0, // U+1FC0 GREEK PERISPOMENI U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI U_GREEK_PSILI_AND_VARIA = 0x1FCD, // U+1FCD GREEK PSILI AND VARIA U_GREEK_PSILI_AND_OXIA = 0x1FCE, // U+1FCE GREEK PSILI AND OXIA U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, // U+1FCF GREEK PSILI AND PERISPOMENI U_GREEK_DASIA_AND_VARIA = 0x1FDD, // U+1FDD GREEK DASIA AND VARIA U_GREEK_DASIA_AND_OXIA = 0x1FDE, // U+1FDE GREEK DASIA AND OXIA U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, // U+1FDF GREEK DASIA AND PERISPOMENI U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, // U+1FED GREEK DIALYTIKA AND VARIA U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, // U+1FEE GREEK DIALYTIKA AND OXIA U_GREEK_VARIA = 0x1FEF, // U+1FEF GREEK VARIA U_GREEK_OXIA = 0x1FFD, // U+1FFD GREEK OXIA U_GREEK_DASIA = 0x1FFE, // U+1FFE GREEK DASIA U_IDEOGRAPHIC_FULL_STOP = 0x3002, // U+3002 IDEOGRAPHIC FULL STOP U_LEFT_CORNER_BRACKET = 0x300C, // U+300C LEFT CORNER BRACKET U_RIGHT_CORNER_BRACKET = 0x300D, // U+300D RIGHT CORNER BRACKET U_LEFT_BLACK_LENTICULAR_BRACKET = 0x3010, // U+3010 LEFT BLACK LENTICULAR BRACKET U_RIGHT_BLACK_LENTICULAR_BRACKET = 0x3011, // U+3011 RIGHT BLACK LENTICULAR BRACKET U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE' /** * UTF-8 BOM * Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF) * http://www.fileformat.info/info/unicode/char/feff/index.htm */ UTF8_BOM = 65279, U_FULLWIDTH_SEMICOLON = 0xFF1B, // U+FF1B FULLWIDTH SEMICOLON U_FULLWIDTH_COMMA = 0xFF0C, // U+FF0C FULLWIDTH COMMA } function roundFloat(number: number, decimalPoints: number): number { const decimal = Math.pow(10, decimalPoints); return Math.round(number * decimal) / decimal; } export class RGBA { _rgbaBrand: void = undefined; /** * Red: integer in [0-255] */ readonly r: number; /** * Green: integer in [0-255] */ readonly g: number; /** * Blue: integer in [0-255] */ readonly b: number; /** * Alpha: float in [0-1] */ readonly a: number; constructor(r: number, g: number, b: number, a: number = 1) { this.r = Math.min(255, Math.max(0, r)) | 0; this.g = Math.min(255, Math.max(0, g)) | 0; this.b = Math.min(255, Math.max(0, b)) | 0; this.a = roundFloat(Math.max(Math.min(1, a), 0), 3); } static equals(a: RGBA, b: RGBA): boolean { return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a; } } export class HSLA { _hslaBrand: void = undefined; /** * Hue: integer in [0, 360] */ readonly h: number; /** * Saturation: float in [0, 1] */ readonly s: number; /** * Luminosity: float in [0, 1] */ readonly l: number; /** * Alpha: float in [0, 1] */ readonly a: number; constructor(h: number, s: number, l: number, a: number) { this.h = Math.max(Math.min(360, h), 0) | 0; this.s = roundFloat(Math.max(Math.min(1, s), 0), 3); this.l = roundFloat(Math.max(Math.min(1, l), 0), 3); this.a = roundFloat(Math.max(Math.min(1, a), 0), 3); } static equals(a: HSLA, b: HSLA): boolean { return a.h === b.h && a.s === b.s && a.l === b.l && a.a === b.a; } /** * Converts an RGB color value to HSL. Conversion formula * adapted from http://en.wikipedia.org/wiki/HSL_color_space. * Assumes r, g, and b are contained in the set [0, 255] and * returns h in the set [0, 360], s, and l in the set [0, 1]. */ static fromRGBA(rgba: RGBA): HSLA { const r = rgba.r / 255; const g = rgba.g / 255; const b = rgba.b / 255; const a = rgba.a; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h = 0; let s = 0; const l = (min + max) / 2; const chroma = max - min; if (chroma > 0) { s = Math.min((l <= 0.5 ? chroma / (2 * l) : chroma / (2 - (2 * l))), 1); switch (max) { case r: h = (g - b) / chroma + (g < b ? 6 : 0); break; case g: h = (b - r) / chroma + 2; break; case b: h = (r - g) / chroma + 4; break; } h *= 60; h = Math.round(h); } return new HSLA(h, s, l, a); } private static _hue2rgb(p: number, q: number, t: number): number { if (t < 0) { t += 1; } 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; } return p; } /** * Converts an HSL color value to RGB. Conversion formula * adapted from http://en.wikipedia.org/wiki/HSL_color_space. * Assumes h in the set [0, 360] s, and l are contained in the set [0, 1] and * returns r, g, and b in the set [0, 255]. */ static toRGBA(hsla: HSLA): RGBA { const h = hsla.h / 360; const { s, l, a } = hsla; let r: number, g: number, b: number; if (s === 0) { r = g = b = l; // achromatic } else { const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = HSLA._hue2rgb(p, q, h + 1 / 3); g = HSLA._hue2rgb(p, q, h); b = HSLA._hue2rgb(p, q, h - 1 / 3); } return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a); } } export class HSVA { _hsvaBrand: void = undefined; /** * Hue: integer in [0, 360] */ readonly h: number; /** * Saturation: float in [0, 1] */ readonly s: number; /** * Value: float in [0, 1] */ readonly v: number; /** * Alpha: float in [0, 1] */ readonly a: number; constructor(h: number, s: number, v: number, a: number) { this.h = Math.max(Math.min(360, h), 0) | 0; this.s = roundFloat(Math.max(Math.min(1, s), 0), 3); this.v = roundFloat(Math.max(Math.min(1, v), 0), 3); this.a = roundFloat(Math.max(Math.min(1, a), 0), 3); } static equals(a: HSVA, b: HSVA): boolean { return a.h === b.h && a.s === b.s && a.v === b.v && a.a === b.a; } // from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm static fromRGBA(rgba: RGBA): HSVA { const r = rgba.r / 255; const g = rgba.g / 255; const b = rgba.b / 255; const cmax = Math.max(r, g, b); const cmin = Math.min(r, g, b); const delta = cmax - cmin; const s = cmax === 0 ? 0 : (delta / cmax); let m: number; if (delta === 0) { m = 0; } else if (cmax === r) { m = ((((g - b) / delta) % 6) + 6) % 6; } else if (cmax === g) { m = ((b - r) / delta) + 2; } else { m = ((r - g) / delta) + 4; } return new HSVA(Math.round(m * 60), s, cmax, rgba.a); } // from http://www.rapidtables.com/convert/color/hsv-to-rgb.htm static toRGBA(hsva: HSVA): RGBA { const { h, s, v, a } = hsva; const c = v * s; const x = c * (1 - Math.abs((h / 60) % 2 - 1)); const m = v - c; let [r, g, b] = [0, 0, 0]; if (h < 60) { r = c; g = x; } else if (h < 120) { r = x; g = c; } else if (h < 180) { g = c; b = x; } else if (h < 240) { g = x; b = c; } else if (h < 300) { r = x; b = c; } else if (h <= 360) { r = c; b = x; } r = Math.round((r + m) * 255); g = Math.round((g + m) * 255); b = Math.round((b + m) * 255); return new RGBA(r, g, b, a); } } export class Color { static fromHex(hex: string): Color { return Color.Format.CSS.parseHex(hex) || Color.red; } readonly rgba: RGBA; private _hsla?: HSLA; get hsla(): HSLA { if (this._hsla) { return this._hsla; } else { return HSLA.fromRGBA(this.rgba); } } private _hsva?: HSVA; get hsva(): HSVA { if (this._hsva) { return this._hsva; } return HSVA.fromRGBA(this.rgba); } constructor(arg: RGBA | HSLA | HSVA) { if (!arg) { throw new Error('Color needs a value'); } else if (arg instanceof RGBA) { this.rgba = arg; } else if (arg instanceof HSLA) { this._hsla = arg; this.rgba = HSLA.toRGBA(arg); } else if (arg instanceof HSVA) { this._hsva = arg; this.rgba = HSVA.toRGBA(arg); } else { throw new Error('Invalid color ctor argument'); } } equals(other: Color | null): boolean { return !!other && RGBA.equals(this.rgba, other.rgba) && HSLA.equals(this.hsla, other.hsla) && HSVA.equals(this.hsva, other.hsva); } /** * http://www.w3.org/TR/WCAG20/#relativeluminancedef * Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white. */ getRelativeLuminance(): number { const R = Color._relativeLuminanceForComponent(this.rgba.r); const G = Color._relativeLuminanceForComponent(this.rgba.g); const B = Color._relativeLuminanceForComponent(this.rgba.b); const luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B; return roundFloat(luminance, 4); } private static _relativeLuminanceForComponent(color: number): number { const c = color / 255; return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4); } /** * http://www.w3.org/TR/WCAG20/#contrast-ratiodef * Returns the contrast ration number in the set [1, 21]. */ getContrastRatio(another: Color): number { const lum1 = this.getRelativeLuminance(); const lum2 = another.getRelativeLuminance(); return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05); } /** * http://24ways.org/2010/calculating-color-contrast * Return 'true' if darker color otherwise 'false' */ isDarker(): boolean { const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000; return yiq < 128; } /** * http://24ways.org/2010/calculating-color-contrast * Return 'true' if lighter color otherwise 'false' */ isLighter(): boolean { const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000; return yiq >= 128; } isLighterThan(another: Color): boolean { const lum1 = this.getRelativeLuminance(); const lum2 = another.getRelativeLuminance(); return lum1 > lum2; } isDarkerThan(another: Color): boolean { const lum1 = this.getRelativeLuminance(); const lum2 = another.getRelativeLuminance(); return lum1 < lum2; } lighten(factor: number): Color { return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l + this.hsla.l * factor, this.hsla.a)); } darken(factor: number): Color { return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l - this.hsla.l * factor, this.hsla.a)); } transparent(factor: number): Color { const { r, g, b, a } = this.rgba; return new Color(new RGBA(r, g, b, a * factor)); } isTransparent(): boolean { return this.rgba.a === 0; } isOpaque(): boolean { return this.rgba.a === 1; } opposite(): Color { return new Color(new RGBA(255 - this.rgba.r, 255 - this.rgba.g, 255 - this.rgba.b, this.rgba.a)); } blend(c: Color): Color { const rgba = c.rgba; // Convert to 0..1 opacity const thisA = this.rgba.a; const colorA = rgba.a; const a = thisA + colorA * (1 - thisA); if (a < 1e-6) { return Color.transparent; } const r = this.rgba.r * thisA / a + rgba.r * colorA * (1 - thisA) / a; const g = this.rgba.g * thisA / a + rgba.g * colorA * (1 - thisA) / a; const b = this.rgba.b * thisA / a + rgba.b * colorA * (1 - thisA) / a; return new Color(new RGBA(r, g, b, a)); } makeOpaque(opaqueBackground: Color): Color { if (this.isOpaque() || opaqueBackground.rgba.a !== 1) { // only allow to blend onto a non-opaque color onto a opaque color return this; } const { r, g, b, a } = this.rgba; // https://stackoverflow.com/questions/12228548/finding-equivalent-color-with-opacity return new Color(new RGBA( opaqueBackground.rgba.r - a * (opaqueBackground.rgba.r - r), opaqueBackground.rgba.g - a * (opaqueBackground.rgba.g - g), opaqueBackground.rgba.b - a * (opaqueBackground.rgba.b - b), 1 )); } flatten(...backgrounds: Color[]): Color { const background = backgrounds.reduceRight((accumulator, color) => { return Color._flatten(color, accumulator); }); return Color._flatten(this, background); } private static _flatten(foreground: Color, background: Color) { const backgroundAlpha = 1 - foreground.rgba.a; return new Color(new RGBA( backgroundAlpha * background.rgba.r + foreground.rgba.a * foreground.rgba.r, backgroundAlpha * background.rgba.g + foreground.rgba.a * foreground.rgba.g, backgroundAlpha * background.rgba.b + foreground.rgba.a * foreground.rgba.b )); } private _toString?: string; toString(): string { this._toString ??= Color.Format.CSS.format(this); return this._toString; } static getLighterColor(of: Color, relative: Color, factor?: number): Color { if (of.isLighterThan(relative)) { return of; } factor = factor ? factor : 0.5; const lum1 = of.getRelativeLuminance(); const lum2 = relative.getRelativeLuminance(); factor = factor * (lum2 - lum1) / lum2; return of.lighten(factor); } static getDarkerColor(of: Color, relative: Color, factor?: number): Color { if (of.isDarkerThan(relative)) { return of; } factor = factor ? factor : 0.5; const lum1 = of.getRelativeLuminance(); const lum2 = relative.getRelativeLuminance(); factor = factor * (lum1 - lum2) / lum1; return of.darken(factor); } static readonly white = new Color(new RGBA(255, 255, 255, 1)); static readonly black = new Color(new RGBA(0, 0, 0, 1)); static readonly red = new Color(new RGBA(255, 0, 0, 1)); static readonly blue = new Color(new RGBA(0, 0, 255, 1)); static readonly green = new Color(new RGBA(0, 255, 0, 1)); static readonly cyan = new Color(new RGBA(0, 255, 255, 1)); static readonly lightgrey = new Color(new RGBA(211, 211, 211, 1)); static readonly transparent = new Color(new RGBA(0, 0, 0, 0)); } export namespace Color { export namespace Format { export namespace CSS { export function formatRGB(color: Color): string { if (color.rgba.a === 1) { return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`; } return Color.Format.CSS.formatRGBA(color); } export function formatRGBA(color: Color): string { return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`; } export function formatHSL(color: Color): string { if (color.hsla.a === 1) { return `hsl(${color.hsla.h}, ${(color.hsla.s * 100).toFixed(2)}%, ${(color.hsla.l * 100).toFixed(2)}%)`; } return Color.Format.CSS.formatHSLA(color); } export function formatHSLA(color: Color): string { return `hsla(${color.hsla.h}, ${(color.hsla.s * 100).toFixed(2)}%, ${(color.hsla.l * 100).toFixed(2)}%, ${color.hsla.a.toFixed(2)})`; } function _toTwoDigitHex(n: number): string { const r = n.toString(16); return r.length !== 2 ? '0' + r : r; } /** * Formats the color as #RRGGBB */ export function formatHex(color: Color): string { return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}`; } /** * Formats the color as #RRGGBBAA * If 'compact' is set, colors without transparancy will be printed as #RRGGBB */ export function formatHexA(color: Color, compact = false): string { if (compact && color.rgba.a === 1) { return Color.Format.CSS.formatHex(color); } return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(Math.round(color.rgba.a * 255))}`; } /** * The default format will use HEX if opaque and RGBA otherwise. */ export function format(color: Color): string { if (color.isOpaque()) { return Color.Format.CSS.formatHex(color); } return Color.Format.CSS.formatRGBA(color); } /** * Converts an Hex color value to a Color. * returns r, g, and b are contained in the set [0, 255] * @param hex string (#RGB, #RGBA, #RRGGBB or #RRGGBBAA). */ export function parseHex(hex: string): Color | null { const length = hex.length; if (length === 0) { // Invalid color return null; } if (hex.charCodeAt(0) !== CharCode.Hash) { // Does not begin with a # return null; } if (length === 7) { // #RRGGBB format const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2)); const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4)); const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6)); return new Color(new RGBA(r, g, b, 1)); } if (length === 9) { // #RRGGBBAA format const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2)); const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4)); const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6)); const a = 16 * _parseHexDigit(hex.charCodeAt(7)) + _parseHexDigit(hex.charCodeAt(8)); return new Color(new RGBA(r, g, b, a / 255)); } if (length === 4) { // #RGB format const r = _parseHexDigit(hex.charCodeAt(1)); const g = _parseHexDigit(hex.charCodeAt(2)); const b = _parseHexDigit(hex.charCodeAt(3)); return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b)); } if (length === 5) { // #RGBA format const r = _parseHexDigit(hex.charCodeAt(1)); const g = _parseHexDigit(hex.charCodeAt(2)); const b = _parseHexDigit(hex.charCodeAt(3)); const a = _parseHexDigit(hex.charCodeAt(4)); return new Color(new RGBA(16 * r + r, 16 * g + g, 16 * b + b, (16 * a + a) / 255)); } // Invalid color return null; } function _parseHexDigit(charCode: CharCode): number { switch (charCode) { case CharCode.Digit0: return 0; case CharCode.Digit1: return 1; case CharCode.Digit2: return 2; case CharCode.Digit3: return 3; case CharCode.Digit4: return 4; case CharCode.Digit5: return 5; case CharCode.Digit6: return 6; case CharCode.Digit7: return 7; case CharCode.Digit8: return 8; case CharCode.Digit9: return 9; case CharCode.a: return 10; case CharCode.A: return 10; case CharCode.b: return 11; case CharCode.B: return 11; case CharCode.c: return 12; case CharCode.C: return 12; case CharCode.d: return 13; case CharCode.D: return 13; case CharCode.e: return 14; case CharCode.E: return 14; case CharCode.f: return 15; case CharCode.F: return 15; } return 0; } } } }