diff --git a/scripts/hud/cgaz.js b/scripts/hud/cgaz.js index cf267a09..ca5d1e72 100644 --- a/scripts/hud/cgaz.js +++ b/scripts/hud/cgaz.js @@ -697,7 +697,7 @@ class Cgaz { mapAngleToScreenDist(velocityAngle, this.hFov, this.screenX, this.scale, this.projection) - this.compassArrowSize; this.compassArrow.style.position = `${this.NaNCheck(leftEdge, 0)}px 0px 0px`; - this.compassArrowIcon.style.washColor = getRgbFromRgba(color); + this.compassArrowIcon.style.washColor = rgbaStringToRgb(color); } this.compassArrow.visible = this.compassMode % 2 && speed >= this.accelMinSpeed; this.tickContainer.visible = this.compassMode > 1; @@ -911,7 +911,7 @@ class Cgaz { this.updateZone(zone, left, right, 0, snapClass, this.snapSplitZone); if (this.snapColorMode) { - snapColor = colorLerp(this.snapSlowColor, this.snapFastColor, alpha); + snapColor = rgbaTupleLerp(this.snapSlowColor, this.snapFastColor, alpha); } let bHighlight = false; @@ -1099,7 +1099,7 @@ class Cgaz { if (gain < 0) { zone.color = this.primeLossColor; } else if (this.primeColorgainEnable) { - zone.color = colorLerp(this.primeAltColor, this.primeGainColor, gainFactor); + zone.color = rgbaTupleLerp(this.primeAltColor, this.primeGainColor, gainFactor); } else { zone.color = this.primeGainColor; } @@ -1248,7 +1248,7 @@ class Cgaz { arrowIcon.style.height = this.NaNCheck(width, 0) + 'px'; arrowIcon.style.width = this.NaNCheck(width, 0) + 'px'; - arrowIcon.style.washColor = getRgbFromRgba(color); + arrowIcon.style.washColor = rgbaStringToRgb(color); arrowIcon.style.overflow = 'noclip noclip'; arrowIcon.style.verticalAlign = align; } diff --git a/scripts/util/colors.js b/scripts/util/colors.js deleted file mode 100644 index 25ef04fe..00000000 --- a/scripts/util/colors.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Utility functions for Javascript. - * Could be ported to C++ and exposed globally in the future. - */ - -/** - * Returns a string formatted: - * rgba(R, G, B, A) - * from color array, where input array elements are range [0, 255]. - * R, G, B values ranged [0, 255], A ranged [0, 1]. - * @param {array} color - * @returns {string} - */ -function getColorStringFromArray(color) { - return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`; -} - -/** - * Returns a array of RGBA values ranged [0, 255]. - * Input string must be formatted: - * rgba(R, G, B, A) - * where R, G, B values ranged [0, 255], A ranged [0, 1]. - * @param {array} color - * @returns {string} - */ -function splitColorString(string) { - return string - .slice(5, -1) - .split(',') - .map((c, i) => (i === 3 ? Number.parseInt(c * 255) : Number.parseInt(c))); -} - -/** - * Blends two colors linearly (not HSV lerp). - * RGB inputs are converted to RGBA with alpha value of 1. - * @param {string} colorA - * @param {string} colorB - * @param {number} alpha - * @returns {string} - */ -function colorLerp(colorA, colorB, alpha) { - const arrayA = splitColorString(colorA); - const arrayB = splitColorString(colorB); - const interp = Math.max(Math.min(alpha, 1), 0); - if (arrayA.length === 3) arrayA.push(255); - if (arrayB.length === 3) arrayB.push(255); - return getColorStringFromArray(arrayA.map((Ai, i) => Ai + interp * (arrayB[i] - Ai))); -} - -/** - * Removes A value from string formatted: - * rgba(R, G, B, A) - * @param {string} colorString - * @returns {string} - */ -function getRgbFromRgba(colorString) { - const [r, g, b] = splitColorString(colorString); - return `rgb(${r}, ${g}, ${b})`; -} - -/** - * Compresses A value from string formatted: - * rgba(R, G, B, A) - * to the range [0.75, 1] - * @param {string} colorString - * @returns {string} - */ -function enhanceAlpha(colorString) { - const [r, g, b, a] = splitColorString(colorString); - return getColorStringFromArray([r, g, b, Math.min(0.25 * a + 192, 255)]); -} diff --git a/scripts/util/colors.ts b/scripts/util/colors.ts new file mode 100644 index 00000000..d5a97c84 --- /dev/null +++ b/scripts/util/colors.ts @@ -0,0 +1,103 @@ +/** + * Functions for manipulating RGB/RGBA strings and tuples. + */ + +type RgbTuple = [number, number, number]; +type RgbaTuple = [number, number, number, number]; + +// __brand is a compile time-only type trick to make these two incompatible, +// even though they're really just `string`s. +type RgbString = string & { __brand: 'rgb' }; +type RgbaString = string & { __brand: 'rgba' }; + +/** + * Returns a string formatted: `rgb(R, G, B, A)` from RGB number tuple, where + * `r`, `g`, `b` are values ranged [0, 255]. + */ +function rgbTupleToString([r, g, b]: RgbTuple): RgbString { + return `rgb(${r}, ${g}, ${b})` as RgbString; +} + +/** + * Returns a string formatted `rgba(R, G, B, A)` from RGBA number tuple, where + * `R`, `G`, `B` are values ranged [0, 255], `A` ranged [0, 1]. + */ +function rgbaTupleToString([r, g, b, a]: RgbaTuple): RgbaString { + return `rgba(${r}, ${g}, ${b}, ${a / 255})` as RgbaString; +} + +/** + * Returns an corresponding RGB tuple for an RGB string. + * Input string must be formatted as `rgb(R, G, A)`, where where `R`, `G`, `B` + * are values ranged `[0, 255]`, `A` ranged `[0, 1]`. + * + * For performance, this function does not check the input string. + */ + +function rgbStringToTuple(str: RgbString): RgbTuple { + return str + .slice(4, -1) + .split(',') + .map((c) => Number.parseInt(c)) as RgbTuple; +} + +/** + * Returns an corresponding RGB tuple for an RGB string. + * Input string must be formatted as `rgb(R, G, A)`, where where `R`, `G`, `B` + * are values ranged `[0, 255]`, `A` ranged `[0, 1]`. + * + * For performance, this function does not check the input string. + */ + +function rgbaStringToTuple(str: RgbaString): RgbaTuple { + return str + .slice(5, -1) + .split(',') + .map((c, i) => (i === 3 ? Number.parseInt(c) * 255 : Number.parseInt(c))) as RgbaTuple; +} +/** + * Blends two colors linearly (not HSV lerp). + * RGB inputs are converted to RGBA with alpha value of 1. + */ +function rgbaTupleLerp(colorA: T, colorB: T, alpha: number): RgbaTuple { + const interp = Math.max(Math.min(alpha, 1), 0); + if (colorA.length === 3) colorA.push(255); + if (colorB.length === 3) colorB.push(255); + return colorA.map((Ai, i) => Ai + interp * (colorB[i] - Ai)) as RgbaTuple; +} + +/** + * Blends two colors linearly (not HSV lerp). + * RGB inputs are converted to RGBA with alpha value of 1. + */ +function rgbaStringLerp(colorA: RgbaString, colorB: RgbaString, alpha: number): RgbaString { + const arrayA = rgbaStringToTuple(colorA); + const arrayB = rgbaStringToTuple(colorB); + return rgbaTupleToString(rgbaTupleLerp(arrayA, arrayB, alpha)); +} + +/** + * Blends two colors linearly (not HSV lerp). + * RGB inputs are converted to RGBA with alpha value of 1. + */ +function rgbStringLerp(colorA: RgbString, colorB: RgbString, alpha: number): RgbString { + const arrayA = rgbStringToTuple(colorA); + const arrayB = rgbStringToTuple(colorB); + return rgbaStringToRgb(rgbaTupleToString(rgbaTupleLerp(arrayA, arrayB, alpha))); +} + +/** + * Removes A value from string formatted `rgba(R, G, B, A)`. + */ +function rgbaStringToRgb(str: RgbaString): RgbString { + const [r, g, b] = rgbaStringToTuple(str); + return `rgb(${r}, ${g}, ${b})` as RgbString; +} + +/** + * Compresses A value from string formatted `rgba(R, G, B, A)` to the range `[0.75, 1]` + */ +function enhanceAlpha(str: RgbaString): RgbaString { + const [r, g, b, a] = rgbaStringToTuple(str); + return rgbaTupleToString([r, g, b, Math.min(0.25 * a + 192, 255)]); +}