Add some wip stub for HSV color picker

main
kts of kettek 2024-02-21 21:07:22 -08:00
parent 309aebdc8e
commit 988e171f22
5 changed files with 213 additions and 3 deletions

View File

@ -4,7 +4,7 @@
import Importer from './sections/Importer.svelte';
import PaletteSection from './sections/Palette.svelte'
import FloatingPanel from './components/FloatingPanel.svelte'
import { Palette, PaletteEntry, defaultPalette } from './types/palette'
import { Palette, PaletteEntry, defaultPalette, type Color } from './types/palette'
import { LoadedFile, PixelsPlaceUndoable, SelectionClearUndoable, SelectionSetUndoable } from './types/file'
@ -23,12 +23,19 @@
import Shortcuts from './components/Shortcuts.svelte'
import { CopyPaste } from './types/copypaste'
import type { PixelPosition } from './types/shapes.js';
import ColorSelector from './components/ColorSelector.svelte';
let theme: 'white'|'g10'|'g80'|'g90'|'g100' = 'g90'
let palette: Palette = defaultPalette()
let primaryColorIndex: number = 1
let secondaryColorIndex: number = 0
let primaryColor: Color = {r: 0, g: 0, b: 0, a: 0}
let secondaryColor: Color = {r: 0, g: 0, b: 0, a: 0}
$: primaryColor = palette?.[primaryColorIndex]
$: secondaryColor = palette?.[secondaryColorIndex]
// Oh no, what are you doing, step palette~
function stepPalette(step: number, primary: boolean) {
@ -163,6 +170,7 @@
<section class='content'>
<section class='left'>
<PaletteSection bind:palette bind:primaryColorIndex bind:secondaryColorIndex file={focusedFile} />
<ColorSelector />
</section>
<menu class='toolbar'>
<Button isSelected={currentTool === toolMove} kind="ghost" size="small" icon={Move} iconDescription="move" tooltipPosition="right" on:click={()=>swapTool(toolMove)}></Button>
@ -271,8 +279,9 @@
}
.left {
display: flex;
flex-direction: row;
align-items: flex-start;
flex-direction: column;
align-items: stretch;
justify-content: space-between;
}
.toolbar {
display: flex;

View File

@ -0,0 +1,71 @@
<script lang='ts'>
export let red: number = 255
export let green: number = 0
export let blue: number = 0
export let alpha: number = 0
let hue: number = 0
let saturation: number = 0
let value: number = 0
</script>
<main>
<div class="hsv">
<div class='hsv_hue' style="background: rgb({red}, {green}, {blue})"></div>
<div class='hsv_saturation'></div>
<div class='hsv_value'></div>
</div>
<div class='sv'>
</div>
<div class='slider'>
<div class='hue' style='width: 100%; height: 1em;'></div>
</div>
<div class='slider'>
<span class="checkerboard"></span>
<div class='alpha' style='width: 100%; height: 1em;'></div>
</div>
</main>
<style>
.slider {
position: relative;
width: 100%;
display: flex;
}
.hsv {
min-height: 9em;
width: 100%;
border: 1px solid red;
}
.hue {
background-image: linear-gradient(to left,
#ff0000, #ff0080,
#ff00ff, #8000ff,
#0000ff, #0080ff,
#00ffff, #00ff80,
#00ff00, #80ff00,
#ffff00, #ff8000,
#ff0000
);
}
.alpha {
background-image: linear-gradient(to right,
transparent 0%,
rgba(255, 0, 0, 1) 100%
);
z-index: 0;
}
.checkerboard {
pointer-events: none;
position: absolute;
z-index: 0;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%);
background-size: 10px 10px;
background-position: 0 0, 0 5px, 5px -5px, -5px 0px;
}
</style>

View File

@ -1,4 +1,5 @@
<script lang='ts'>
import type { Color } from '../types/palette'
import type { LoadedFile } from '../types/file'
export let file: LoadedFile

View File

@ -0,0 +1,122 @@
function floatsToBytes(colors: number[]): number[] {
if (colors.length <= 0) return colors
if (colors[0] <= 1.0) {
colors[0] = colors[0] * 255
colors[1] = colors[1] * 255
colors[2] = colors[2] * 255
}
return colors
}
function bytesToFloats(colors: number[]): number[] {
if (colors.length <= 0) return colors
if (colors[0] > 1.0) {
colors[0] = colors[0] / 255
colors[1] = colors[1] / 255
colors[2] = colors[2] / 255
}
return colors
}
export function HSV2RGB(hsv: number[]): number[] {
let hh: number, p: number, q: number, t: number, ff: number, i: number
let rgb: number[] = []
if (hsv[1] <= 0.0) {
rgb[0] = hsv[2]
rgb[1] = hsv[2]
rgb[2] = hsv[2]
return floatsToBytes(rgb)
}
hh = hsv[0]
if (hh >= 360.0) {
hh = 0.0
}
hh /= 60.0
i = Math.floor(hh)
ff = hh - i
p = hsv[2] * (1.0 - hsv[1])
q = hsv[2] * (1.0 - (hsv[1] * ff))
t = hsv[2] * (1.0 - (hsv[1] * (1.0 - ff)))
switch (i) {
case 0:
rgb[0] = hsv[2]
rgb[1] = t
rgb[2] = p
break
case 1:
rgb[0] = q
rgb[1] = hsv[2]
rgb[2] = p
break
case 2:
rgb[0] = p
rgb[1] = hsv[2]
rgb[2] = t
break
case 3:
rgb[0] = p
rgb[1] = q
rgb[2] = hsv[2]
break
case 4:
rgb[0] = t
rgb[1] = p
rgb[2] = hsv[2]
break
case 5:
default:
rgb[0] = hsv[2]
rgb[1] = p
rgb[2] = q
break
}
return floatsToBytes(rgb)
}
export function RGB2HSV(rgb: number[]): number[] {
rgb = bytesToFloats(rgb)
let min: number, max: number, delta: number
let hsv: number[] = []
min = rgb[0] < rgb[1] ? rgb[0] : rgb[1]
min = min < rgb[2] ? min : rgb[2]
max = rgb[0] > rgb[1] ? rgb[0] : rgb[1]
max = max > rgb[2] ? max : rgb[2]
hsv[2] = max
delta = max - min
if (delta < 0.00001) {
hsv[1] = 0
hsv[0] = 0
return hsv
}
if (max > 0.0) {
hsv[1] = delta / max
} else {
hsv[1] = 0.0
hsv[0] = NaN
return hsv
}
if (rgb[0] >= max) {
hsv[0] = ( rgb[1] - rgb[2] ) / delta
} else if (rgb[1] >= max) {
hsv[0] = 2.0 + ( rgb[2] - rgb[0] ) / delta
} else {
hsv[0] = 4.0 + ( rgb[0] - rgb[1] ) / delta
}
hsv[0] *= 60.0;
if (hsv[0] < 0.0) {
hsv[0] += 360.0
}
return hsv
}

View File

@ -15,3 +15,10 @@ export const defaultPalette = (): Palette => {
]
}
}
export interface Color {
r: number;
g: number;
b: number;
a: number;
}