Add some wip stub for HSV color picker
parent
309aebdc8e
commit
988e171f22
|
|
@ -4,7 +4,7 @@
|
||||||
import Importer from './sections/Importer.svelte';
|
import Importer from './sections/Importer.svelte';
|
||||||
import PaletteSection from './sections/Palette.svelte'
|
import PaletteSection from './sections/Palette.svelte'
|
||||||
import FloatingPanel from './components/FloatingPanel.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'
|
import { LoadedFile, PixelsPlaceUndoable, SelectionClearUndoable, SelectionSetUndoable } from './types/file'
|
||||||
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
import Shortcuts from './components/Shortcuts.svelte'
|
import Shortcuts from './components/Shortcuts.svelte'
|
||||||
import { CopyPaste } from './types/copypaste'
|
import { CopyPaste } from './types/copypaste'
|
||||||
import type { PixelPosition } from './types/shapes.js';
|
import type { PixelPosition } from './types/shapes.js';
|
||||||
|
import ColorSelector from './components/ColorSelector.svelte';
|
||||||
|
|
||||||
let theme: 'white'|'g10'|'g80'|'g90'|'g100' = 'g90'
|
let theme: 'white'|'g10'|'g80'|'g90'|'g100' = 'g90'
|
||||||
|
|
||||||
|
|
@ -30,6 +31,12 @@
|
||||||
let primaryColorIndex: number = 1
|
let primaryColorIndex: number = 1
|
||||||
let secondaryColorIndex: number = 0
|
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~
|
// Oh no, what are you doing, step palette~
|
||||||
function stepPalette(step: number, primary: boolean) {
|
function stepPalette(step: number, primary: boolean) {
|
||||||
if (primary) {
|
if (primary) {
|
||||||
|
|
@ -163,6 +170,7 @@
|
||||||
<section class='content'>
|
<section class='content'>
|
||||||
<section class='left'>
|
<section class='left'>
|
||||||
<PaletteSection bind:palette bind:primaryColorIndex bind:secondaryColorIndex file={focusedFile} />
|
<PaletteSection bind:palette bind:primaryColorIndex bind:secondaryColorIndex file={focusedFile} />
|
||||||
|
<ColorSelector />
|
||||||
</section>
|
</section>
|
||||||
<menu class='toolbar'>
|
<menu class='toolbar'>
|
||||||
<Button isSelected={currentTool === toolMove} kind="ghost" size="small" icon={Move} iconDescription="move" tooltipPosition="right" on:click={()=>swapTool(toolMove)}></Button>
|
<Button isSelected={currentTool === toolMove} kind="ghost" size="small" icon={Move} iconDescription="move" tooltipPosition="right" on:click={()=>swapTool(toolMove)}></Button>
|
||||||
|
|
@ -271,8 +279,9 @@
|
||||||
}
|
}
|
||||||
.left {
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: stretch;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<script lang='ts'>
|
<script lang='ts'>
|
||||||
|
import type { Color } from '../types/palette'
|
||||||
import type { LoadedFile } from '../types/file'
|
import type { LoadedFile } from '../types/file'
|
||||||
|
|
||||||
export let file: LoadedFile
|
export let file: LoadedFile
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -15,3 +15,10 @@ export const defaultPalette = (): Palette => {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Color {
|
||||||
|
r: number;
|
||||||
|
g: number;
|
||||||
|
b: number;
|
||||||
|
a: number;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue