diff --git a/src/lib/setting.ts b/src/lib/setting.ts index 998fab60..6a643617 100644 --- a/src/lib/setting.ts +++ b/src/lib/setting.ts @@ -1,6 +1,85 @@ import type { Action } from "svelte/action"; import { changes, ChangeType, settings } from "$lib/undo-redo"; +/** + * https://gist.github.com/mjackson/5311256 + */ +function rgbToHsv(r: number, g: number, b: number): [number, number, number] { + r /= 255; + g /= 255; + b /= 255; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h = 0; + const v = max; + + const d = max - min; + const s = max == 0 ? 0 : d / max; + + if (max == min) { + h = 0; // achromatic + } else { + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + + h /= 6; + } + + return [Math.floor(h * 0xffff), Math.floor(s * 0xff), Math.floor(v * 0xff)]; +} + +/** + * https://gist.github.com/mjackson/5311256 + */ +function hsvToRgb(h: number, s: number, v: number): [number, number, number] { + h /= 0xffff; + s /= 0xff; + v /= 0xff; + + let r = 0; + let g = 0; + let b = 0; + + const i = Math.floor(h * 6); + const f = h * 6 - i; + const p = v * (1 - s); + const q = v * (1 - f * s); + const t = v * (1 - (1 - f) * s); + + switch (i % 6) { + case 0: + (r = v), (g = t), (b = p); + break; + case 1: + (r = q), (g = v), (b = p); + break; + case 2: + (r = p), (g = v), (b = t); + break; + case 3: + (r = p), (g = q), (b = v); + break; + case 4: + (r = t), (g = p), (b = v); + break; + case 5: + (r = v), (g = p), (b = q); + break; + } + + return [Math.floor(r * 0xff), Math.floor(g * 0xff), Math.floor(b * 0xff)]; +} + export const setting: Action< HTMLInputElement | HTMLSelectElement, { id: number; inverse?: number; scale?: number } @@ -9,7 +88,12 @@ export const setting: Action< { id, inverse, scale }, ) { node.setAttribute("disabled", ""); - const type = node.getAttribute("type") as "number" | "checkbox" | "range"; + const type = node.getAttribute("type") as + | "number" + | "checkbox" + | "range" + | "color"; + const isColor = type === "color"; const isNumeric = type === "number" || type === "range" || node instanceof HTMLSelectElement; const min = node.hasAttribute("min") @@ -30,6 +114,13 @@ export const setting: Action< ? scale * value : value ).toString(); + } else if (isColor) { + const rgb = hsvToRgb( + settings[id]!.value, + settings[id + 1]!.value, + settings[id + 2]!.value, + ); + node.value = `#${rgb.map((c) => c.toString(16).padStart(2, "0")).join("")}`; } else { node.checked = value !== 0; } @@ -58,6 +149,22 @@ export const setting: Action< ? value / scale : value, ); + } else if (isColor) { + const r = parseInt(node.value.slice(1, 3), 16); + const g = parseInt(node.value.slice(3, 5), 16); + const b = parseInt(node.value.slice(5, 7), 16); + const hsv = rgbToHsv(r, g, b); + changes.update((changes) => { + changes.push( + hsv.map((value, i) => ({ + type: ChangeType.Setting, + id: id + i, + setting: value, + })), + ); + return changes; + }); + return; } else { value = node.checked ? 1 : 0; } diff --git a/src/routes/(app)/config/settings/+page.svelte b/src/routes/(app)/config/settings/+page.svelte index 7c0b8681..2e33e63d 100644 --- a/src/routes/(app)/config/settings/+page.svelte +++ b/src/routes/(app)/config/settings/+page.svelte @@ -117,43 +117,49 @@ {/if} {#each category.items as item} {#if item.name !== "enable"} - + {#if item.unit === "H"} + + {:else if item.unit !== "S" && item.unit !== "B"} + + {/if} {/if} {/each}