feat: add chroma key tool and settings, including UI components and controls
This commit is contained in:
@@ -25,6 +25,7 @@ export const commandIds = {
|
||||
selectionAddLayer: "selection.addLayer",
|
||||
toolSetActive: "tool.setActive",
|
||||
toolSetBrushSettings: "tool.setBrushSettings",
|
||||
toolSetChromaKeySettings: "tool.setChromaKeySettings",
|
||||
toolSetBrushPreview: "tool.setBrushPreview",
|
||||
toolSetBrushStrokePreview: "tool.setBrushStrokePreview",
|
||||
toolSetMaskViewMode: "tool.setMaskViewMode",
|
||||
|
||||
@@ -54,10 +54,10 @@ export type { CommandRegistry } from "./registry";
|
||||
export { createCommandRegistry } from "./registry";
|
||||
export { selectionAddLayerCommand, selectionClearCommand, selectionCommands, selectionSetCommand } from "./selection";
|
||||
export type { SelectionAddLayerPayload, SelectionSetPayload } from "./selection";
|
||||
export { toolCommands, toolEnterMaskEditCommand, toolEnterTemporaryPanCommand, toolExitMaskEditCommand, toolExitTemporaryPanCommand, toolSetActiveCommand, toolSetBrushPreviewCommand, toolSetBrushSettingsCommand, toolSetBrushStrokePreviewCommand, toolSetMaskViewModeCommand } from "./tool";
|
||||
export { toolCommands, toolEnterMaskEditCommand, toolEnterTemporaryPanCommand, toolExitMaskEditCommand, toolExitTemporaryPanCommand, toolSetActiveCommand, toolSetBrushPreviewCommand, toolSetBrushSettingsCommand, toolSetBrushStrokePreviewCommand, toolSetChromaKeySettingsCommand, toolSetMaskViewModeCommand } from "./tool";
|
||||
export { transformBeginCommand, transformCommands, transformEndCommand, transformSetBoundsCommand, transformUpdateCommand } from "./transform";
|
||||
export type { TransformBeginPayload, TransformSetBoundsPayload, TransformUpdatePayload } from "./transform";
|
||||
export type { ToolEnterMaskEditPayload, ToolSetActivePayload, ToolSetBrushPreviewPayload, ToolSetBrushSettingsPayload, ToolSetBrushStrokePreviewPayload, ToolSetMaskViewModePayload } from "./tool";
|
||||
export type { ToolEnterMaskEditPayload, ToolSetActivePayload, ToolSetBrushPreviewPayload, ToolSetBrushSettingsPayload, ToolSetBrushStrokePreviewPayload, ToolSetChromaKeySettingsPayload, ToolSetMaskViewModePayload } from "./tool";
|
||||
export {
|
||||
viewportCommands,
|
||||
viewportPanCommand,
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
DocumentUngroupLayerPayload,
|
||||
} from "./document";
|
||||
import type { SelectionAddLayerPayload, SelectionSetPayload } from "./selection";
|
||||
import type { ToolEnterMaskEditPayload, ToolSetActivePayload, ToolSetBrushPreviewPayload, ToolSetBrushSettingsPayload, ToolSetBrushStrokePreviewPayload, ToolSetMaskViewModePayload } from "./tool";
|
||||
import type { ToolEnterMaskEditPayload, ToolSetActivePayload, ToolSetBrushPreviewPayload, ToolSetBrushSettingsPayload, ToolSetBrushStrokePreviewPayload, ToolSetChromaKeySettingsPayload, ToolSetMaskViewModePayload } from "./tool";
|
||||
import type { TransformBeginPayload, TransformSetBoundsPayload, TransformUpdatePayload } from "./transform";
|
||||
import type {
|
||||
ViewportFitArtboardPayload,
|
||||
@@ -60,6 +60,7 @@ export type CommandPayloads = {
|
||||
[commandIds.selectionAddLayer]: SelectionAddLayerPayload;
|
||||
[commandIds.toolSetActive]: ToolSetActivePayload;
|
||||
[commandIds.toolSetBrushSettings]: ToolSetBrushSettingsPayload;
|
||||
[commandIds.toolSetChromaKeySettings]: ToolSetChromaKeySettingsPayload;
|
||||
[commandIds.toolSetBrushPreview]: ToolSetBrushPreviewPayload;
|
||||
[commandIds.toolSetBrushStrokePreview]: ToolSetBrushStrokePreviewPayload;
|
||||
[commandIds.toolSetMaskViewMode]: ToolSetMaskViewModePayload;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { createInitialAppState } from "@editor/initial-state";
|
||||
import { toolEnterMaskEditCommand, toolEnterTemporaryPanCommand, toolExitMaskEditCommand, toolExitTemporaryPanCommand, toolSetActiveCommand, toolSetBrushPreviewCommand, toolSetBrushSettingsCommand, toolSetBrushStrokePreviewCommand, toolSetMaskViewModeCommand } from "./tool";
|
||||
import { toolEnterMaskEditCommand, toolEnterTemporaryPanCommand, toolExitMaskEditCommand, toolExitTemporaryPanCommand, toolSetActiveCommand, toolSetBrushPreviewCommand, toolSetBrushSettingsCommand, toolSetBrushStrokePreviewCommand, toolSetChromaKeySettingsCommand, toolSetMaskViewModeCommand } from "./tool";
|
||||
|
||||
const defaultBrush = { color: "#111827", size: 8, hardness: 100 };
|
||||
const defaultChromaKey = { color: "#00ff00", tolerance: 32, softness: 24, feather: 0, choke: 0, despeckle: 0, spill: 50 };
|
||||
|
||||
describe("tool commands", () => {
|
||||
test("sets active tool", () => {
|
||||
const next = toolSetActiveCommand.execute({ state: createInitialAppState("Test") }, { tool: "brush" });
|
||||
expect(next.editor.tools).toEqual({ activeTool: "brush", interactionMode: { type: "tool", tool: "brush" }, brush: defaultBrush });
|
||||
expect(next.editor.tools).toEqual({ activeTool: "brush", interactionMode: { type: "tool", tool: "brush" }, brush: defaultBrush, chromaKey: defaultChromaKey });
|
||||
});
|
||||
|
||||
test("sets brush settings", () => {
|
||||
@@ -16,6 +17,12 @@ describe("tool commands", () => {
|
||||
expect(next.editor.tools.brush).toEqual({ color: "#ff0000", size: 24, hardness: 50 });
|
||||
});
|
||||
|
||||
test("sets chroma key settings", () => {
|
||||
const next = toolSetChromaKeySettingsCommand.execute({ state: createInitialAppState("Test") }, { color: "#123456", tolerance: 300 });
|
||||
|
||||
expect(next.editor.tools.chromaKey).toEqual({ color: "#123456", tolerance: 255, softness: 24, feather: 0, choke: 0, despeckle: 0, spill: 50 });
|
||||
});
|
||||
|
||||
test("sets and clears brush preview", () => {
|
||||
const showing = toolSetBrushPreviewCommand.execute({ state: createInitialAppState("Test") }, { position: { x: 10, y: 20 } });
|
||||
const cleared = toolSetBrushPreviewCommand.execute({ state: showing }, undefined);
|
||||
@@ -56,7 +63,7 @@ describe("tool commands", () => {
|
||||
const panning = toolEnterTemporaryPanCommand.execute({ state: initial }, undefined);
|
||||
const restored = toolExitTemporaryPanCommand.execute({ state: panning }, undefined);
|
||||
|
||||
expect(panning.editor.tools).toEqual({ activeTool: "select", interactionMode: { type: "temporary-pan", previousTool: "select" }, brush: defaultBrush });
|
||||
expect(panning.editor.tools).toEqual({ activeTool: "select", interactionMode: { type: "temporary-pan", previousTool: "select" }, brush: defaultBrush, chromaKey: defaultChromaKey });
|
||||
expect(restored.editor.tools).toEqual(initial.editor.tools);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Vec2D } from "@core/geometry";
|
||||
import type { LayerId, ArtboardId, AssetId } from "@core/id";
|
||||
import type { Layer } from "@core/layer";
|
||||
import type { MaskViewMode } from "@editor/state";
|
||||
import type { BrushSettings, ToolId } from "@editor/tools";
|
||||
import type { BrushSettings, ChromaKeySettings, ToolId } from "@editor/tools";
|
||||
import type { Command } from "./command";
|
||||
import { commandIds } from "./ids";
|
||||
|
||||
@@ -13,6 +13,8 @@ export type ToolSetActivePayload = {
|
||||
|
||||
export type ToolSetBrushSettingsPayload = Partial<BrushSettings>;
|
||||
|
||||
export type ToolSetChromaKeySettingsPayload = Partial<ChromaKeySettings>;
|
||||
|
||||
export type ToolSetBrushPreviewPayload = { position: Vec2D } | undefined;
|
||||
|
||||
export type ToolSetBrushStrokePreviewPayload =
|
||||
@@ -73,6 +75,31 @@ export const toolSetBrushSettingsCommand: Command<ToolSetBrushSettingsPayload> =
|
||||
},
|
||||
};
|
||||
|
||||
export const toolSetChromaKeySettingsCommand: Command<ToolSetChromaKeySettingsPayload> = {
|
||||
id: commandIds.toolSetChromaKeySettings,
|
||||
name: "Set chroma key settings",
|
||||
execute({ state }, payload) {
|
||||
return {
|
||||
...state,
|
||||
editor: {
|
||||
...state.editor,
|
||||
tools: {
|
||||
...state.editor.tools,
|
||||
chromaKey: {
|
||||
color: payload.color ?? state.editor.tools.chromaKey.color,
|
||||
tolerance: clampNumber(payload.tolerance ?? state.editor.tools.chromaKey.tolerance, 0, 255),
|
||||
softness: clampNumber(payload.softness ?? state.editor.tools.chromaKey.softness, 0, 255),
|
||||
feather: clampNumber(payload.feather ?? state.editor.tools.chromaKey.feather, 0, 20),
|
||||
choke: clampNumber(payload.choke ?? state.editor.tools.chromaKey.choke, -20, 20),
|
||||
despeckle: clampNumber(payload.despeckle ?? state.editor.tools.chromaKey.despeckle, 0, 20),
|
||||
spill: clampNumber(payload.spill ?? state.editor.tools.chromaKey.spill, 0, 100),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const toolSetBrushPreviewCommand: Command<ToolSetBrushPreviewPayload> = {
|
||||
id: commandIds.toolSetBrushPreview,
|
||||
name: "Set brush preview",
|
||||
@@ -222,6 +249,7 @@ export const toolExitTemporaryPanCommand: Command = {
|
||||
export const toolCommands = [
|
||||
toolSetActiveCommand,
|
||||
toolSetBrushSettingsCommand,
|
||||
toolSetChromaKeySettingsCommand,
|
||||
toolSetBrushPreviewCommand,
|
||||
toolSetBrushStrokePreviewCommand,
|
||||
toolSetMaskViewModeCommand,
|
||||
|
||||
Reference in New Issue
Block a user