-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add function to calculate triadic colors
This commit adds a function to calculate the triadic colors of a given color across all supported formats (hex, rgb, hsl, etc.). Accompanying unit tests are also included to validate the correctness of these calculations. The new function has been exported through the project's main entry point for use in creating color schemes.
- Loading branch information
1 parent
8c3552a
commit 4369ab6
Showing
3 changed files
with
174 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { detectColorFormat } from '@/parser/detectColorFormat'; | ||
import { decomposeColor } from '@/parser/decomposeColor'; | ||
import { rgbToHsl } from '@/conversions/rgbToHsl'; | ||
import { hslToRgb } from '@/conversions/hslToRgb'; | ||
import { hslaToRgba } from '@/conversions/hslaToRgba'; | ||
import { hexToHsl } from '@/conversions/hexToHsl'; | ||
import { hexAlphaToHsla } from '@/conversions/hexAlphaToHsla'; | ||
import { labToRgb } from '@/conversions/labToRgb'; | ||
import { lchToRgb } from '@/conversions/lchToRgb'; | ||
import { parseColorToRgba } from '@/parser/parseColorToRgba'; | ||
import { rebuildColorFromRgba } from '@/parser/rebuildColorFromRgba'; | ||
import { ColorFormat, ColorFormats } from '@/utils/colorFormats'; | ||
import {rgbToLab} from "@/conversions/rgbToLab"; | ||
import {recomposeColor} from "@/parser/recomposeColor"; | ||
import {rgbToLch} from "@/conversions/rgbToLch"; | ||
|
||
/** | ||
* Finds the triadic colors of a given color. | ||
* | ||
* @param {string} color - The input color string in any supported format (hex, rgb, hsl, etc.). | ||
* @returns {string[]} - The triadic colors in the original format. | ||
* @throws {Error} - Throws an error if the input color format is invalid. | ||
*/ | ||
export function triadicColors(color: string): string[] { | ||
const format: ColorFormat = detectColorFormat(color); | ||
const decomposed = decomposeColor(color); | ||
|
||
let triadicColors: { r: number; g: number; b: number; a?: number }[] = []; | ||
|
||
switch (format) { | ||
case ColorFormats.HSL: { | ||
const { h, s, l } = decomposed; | ||
const h1 = (h + 120) % 360; | ||
const h2 = (h + 240) % 360; | ||
triadicColors = [hslToRgb(h1, s, l, false), hslToRgb(h2, s, l, false)]; | ||
break; | ||
} | ||
|
||
case ColorFormats.HSLA: { | ||
const { h, s, l, a } = decomposed; | ||
const h1 = (h + 120) % 360; | ||
const h2 = (h + 240) % 360; | ||
triadicColors = [hslaToRgba(h1, s, l, a, false), hslaToRgba(h2, s, l, a, false)]; | ||
break; | ||
} | ||
|
||
case ColorFormats.RGB: { | ||
const hsl = rgbToHsl(decomposed.r, decomposed.g, decomposed.b, false); | ||
const h1 = (hsl.h + 120) % 360; | ||
const h2 = (hsl.h + 240) % 360; | ||
return [ | ||
hslToRgb(h1, hsl.s, hsl.l), | ||
hslToRgb(h2, hsl.s, hsl.l), | ||
]; | ||
} | ||
|
||
case ColorFormats.RGBA: { | ||
const hsl = rgbToHsl(decomposed.r, decomposed.g, decomposed.b, false); | ||
const h1 = (hsl.h + 120) % 360; | ||
const h2 = (hsl.h + 240) % 360; | ||
triadicColors = [ | ||
// @ts-ignore | ||
hslToRgb(h1, hsl.s, hsl.l, false), | ||
// @ts-ignore | ||
hslToRgb(h2, hsl.s, hsl.l, false), | ||
]; | ||
triadicColors.forEach((color) => (color.a = decomposed.a)); | ||
break; | ||
} | ||
case ColorFormats.HEX: { | ||
const hsl = hexToHsl(color, false); | ||
const h1 = (hsl.h + 120) % 360; | ||
const h2 = (hsl.h + 240) % 360; | ||
triadicColors = [ | ||
// @ts-ignore | ||
hslToRgb(h1, hsl.s, hsl.l, false), | ||
// @ts-ignore | ||
hslToRgb(h2, hsl.s, hsl.l, false), | ||
]; | ||
break; | ||
} | ||
case ColorFormats.HEX_ALPHA: { | ||
const hsla = hexAlphaToHsla(color, false); | ||
const h1 = (hsla.h + 120) % 360; | ||
const h2 = (hsla.h + 240) % 360; | ||
triadicColors = [ | ||
hslaToRgba(h1, hsla.s, hsla.l, hsla.a, false), | ||
hslaToRgba(h2, hsla.s, hsla.l, hsla.a, false), | ||
]; | ||
break; | ||
} | ||
case ColorFormats.LAB: { | ||
const { l, a, b } = decomposed; | ||
const triadicLabColors = [ | ||
{ l, a: -a, b: -b }, | ||
{ l, a: -a, b: -b + 120 }, | ||
]; | ||
return triadicLabColors.map(lab => recomposeColor(color, lab)); | ||
} | ||
case ColorFormats.LCH: { | ||
const { l, c, h } = decomposed; | ||
const triadicLchColors = [ | ||
{ l, c, h: (h + 120) % 360 }, | ||
{ l, c, h: (h + 240) % 360 }, | ||
]; | ||
return triadicLchColors.map(lch => recomposeColor(color, lch)); | ||
} | ||
case ColorFormats.NAMED: { | ||
const rgba = parseColorToRgba(color); | ||
const hsl = rgbToHsl(rgba.r, rgba.g, rgba.b, false); | ||
const h1 = (hsl.h + 120) % 360; | ||
const h2 = (hsl.h + 240) % 360; | ||
triadicColors = [ | ||
hslToRgb(h1, hsl.s, hsl.l, false), | ||
hslToRgb(h2, hsl.s, hsl.l, false), | ||
]; | ||
break; | ||
} | ||
default: | ||
throw new Error(`Unsupported color format ${color} for triadic color calculation`); | ||
} | ||
|
||
// @ts-ignore | ||
return triadicColors.map((triadicColor) => rebuildColorFromRgba(color, triadicColor)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { triadicColors } from '@/harmony/triadicColors'; | ||
|
||
describe('triadicColors', () => { | ||
test('triadic colors of rgb(255, 0, 0) are rgb(0, 255, 0) and rgb(0, 0, 255)', () => { | ||
const result = triadicColors('rgb(255, 0, 0)'); | ||
expect(result).toEqual(['rgb(0, 255, 0)', 'rgb(0, 0, 255)']); | ||
}); | ||
|
||
test('triadic colors of rgba(255, 0, 0, 1) are rgba(0, 255, 0, 1) and rgba(0, 0, 255, 1)', () => { | ||
const result = triadicColors('rgba(255, 0, 0, 1)'); | ||
expect(result).toEqual(['rgba(0, 255, 0, 1)', 'rgba(0, 0, 255, 1)']); | ||
}); | ||
|
||
test('triadic colors of hsl(0, 100%, 50%) are hsl(120, 100%, 50%) and hsl(240, 100%, 50%)', () => { | ||
const result = triadicColors('hsl(0, 100%, 50%)'); | ||
expect(result).toEqual(['hsl(120, 100%, 50%)', 'hsl(240, 100%, 50%)']); | ||
}); | ||
|
||
test('triadic colors of hsla(0, 100%, 50%, 1) are hsla(120, 100%, 50%, 1) and hsla(240, 100%, 50%, 1)', () => { | ||
const result = triadicColors('hsla(0, 100%, 50%, 1)'); | ||
expect(result).toEqual(['hsla(120, 100%, 50%, 1)', 'hsla(240, 100%, 50%, 1)']); | ||
}); | ||
|
||
test('triadic colors of #ff0000 are #00ff00 and #0000ff', () => { | ||
const result = triadicColors('#ff0000'); | ||
expect(result).toEqual(['#00ff00', '#0000ff']); | ||
}); | ||
|
||
test('triadic colors of #ff0000ff are #00ff00ff and #0000ffff', () => { | ||
const result = triadicColors('#ff0000ff'); | ||
expect(result).toEqual(['#00ff00ff', '#0000ffff']); | ||
}); | ||
|
||
test('triadic colors of lab(50 40 30) are lab(50 -40 -30) and lab(50 -40 90)', () => { | ||
const result = triadicColors('lab(50 40 30)'); | ||
expect(result).toEqual(['lab(50 -40 -30)', 'lab(50 -40 90)']); | ||
}); | ||
|
||
test('triadic colors of lch(50 30 100) are lch(50 30 -20) and lch(50 30 220)', () => { | ||
const result = triadicColors('lch(50 30 100)'); | ||
expect(result).toEqual(['lch(50 30 220)', 'lch(50 30 340)']); | ||
}); | ||
|
||
test('triadic colors of red (named color) are lime and blue', () => { | ||
const result = triadicColors('red'); | ||
expect(result).toEqual(['rgba(0, 255, 0, 1)', 'rgba(0, 0, 255, 1)']); | ||
}); | ||
}); |