diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 7ae89da..d9fe959 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -14,10 +14,10 @@ import { OverflowMenu, OverflowMenuItem } from "carbon-components-svelte" - import { Close, Erase, PaintBrushAlt, RainDrop, Redo, Select_01, Undo, Scale, Eyedropper, Move } from "carbon-icons-svelte" + import { Close, Erase, PaintBrushAlt, RainDrop, Redo, Select_01, Undo, Scale, Eyedropper, Move, MagicWand } from "carbon-icons-svelte" import StackPreview from './sections/StackPreview.svelte' import type { Canvas } from './types/canvas' - import { BrushTool, EraserTool, FillTool, PickerTool, SelectionTool, type BrushType, type Tool, MoveTool } from './types/tools' + import { BrushTool, EraserTool, FillTool, PickerTool, SelectionTool, MagicWandTool, type BrushType, type Tool, MoveTool } from './types/tools' import BrushSize from './components/BrushSize.svelte' import Shortcut from './components/Shortcut.svelte' import Shortcuts from './components/Shortcuts.svelte' @@ -51,6 +51,7 @@ let showPreview: boolean = false let toolSelection = new SelectionTool() + let toolMagicWand = new MagicWandTool() let toolFill = new FillTool() let toolErase = new EraserTool() let toolBrush = new BrushTool() @@ -147,6 +148,7 @@ + @@ -154,6 +156,7 @@ focusedFile?.push(new SelectionClearUndoable())} /> swapTool(toolSelection)} /> + swapTool(toolMagicWand)} /> swapTool(toolMove)} /> toolMove.shift({file: focusedFile}, {x: -1, y: 0, id: 0})} /> toolMove.shift({file: focusedFile}, {x: 1, y: 0, id: 0})} /> diff --git a/frontend/src/types/tools.ts b/frontend/src/types/tools.ts index 9acd7d5..0defa03 100644 --- a/frontend/src/types/tools.ts +++ b/frontend/src/types/tools.ts @@ -282,6 +282,59 @@ export class SelectionTool implements Tool { } } +export class MagicWandTool implements Tool { + private active: boolean + + isActive(): boolean { + return this.active + } + + pointerDown(ctx: ToolContext & FloodToolContext, ptr: Pointer) { + this.active = true + let pixels: { x: number, y: number, marked: boolean }[] = [] + + let traversed = new Set() + + let value = true + let clear = false + if (!ptr.shift && !ptr.control) { + clear = true + } + if (ptr.control) { + value = false + } + + let p = ctx.file.canvas.getPixel(ptr.x, ptr.y) + if (p !== -1) { + let queue = [{x: ptr.x, y: ptr.y}] + while (queue.length > 0) { + let {x, y} = queue.shift() + let index = y * ctx.file.canvas.width + x + if (traversed.has(index)) { + continue + } + traversed.add(index) + let p2 = ctx.file.canvas.getPixel(x, y) + if (p2 === p) { + pixels.push({x, y, marked: value}) + if (x > 0) queue.push({x: x-1, y}) + if (x < ctx.file.canvas.width-1) queue.push({x: x+1, y}) + if (y > 0) queue.push({x, y: y-1}) + if (y < ctx.file.canvas.height-1) queue.push({x, y: y+1}) + } + } + } + + ctx.file.selection.active = true + ctx.file.push(new SelectionSetUndoable(pixels, clear)) + } + pointerMove(ctx: ToolContext & FloodToolContext, ptr: Pointer) { + } + pointerUp(ctx: ToolContext & FloodToolContext, ptr: Pointer) { + this.active = false + } +} + export class MoveTool implements Tool { private active: boolean private startX: number