61 lines
3.9 KiB
TypeScript
61 lines
3.9 KiB
TypeScript
import { commandIds } from "@commands/ids";
|
|
import type { ImageDocument } from "@core/document";
|
|
import type { Vec2D } from "@core/geometry";
|
|
import type { Layer } from "@core/layer";
|
|
import { getLayerMask } from "@core/layer-mask-utils";
|
|
import { resolveTransformTargetBounds } from "@editor/transform-targets";
|
|
import type { AppStore } from "@editor/store";
|
|
import type { EditorState } from "@editor/state";
|
|
import { createWandMask } from "@platform/browser/magicWandRaster";
|
|
|
|
export async function applyMagicWandAt(store: AppStore, point: Vec2D, modeOverride?: EditorState["tools"]["magicWand"]["mode"]) {
|
|
const state = store.getState();
|
|
if (state.editor.tools.activeTool !== "magicWand") return false;
|
|
const target = resolveTarget(state.document, state.editor);
|
|
if (!target) return true;
|
|
const x = Math.floor((point.x - target.layer.transform.position.x) / Math.max(0.0001, target.layer.transform.scale.x));
|
|
const y = Math.floor((point.y - target.layer.transform.position.y) / Math.max(0.0001, target.layer.transform.scale.y));
|
|
if (x < 0 || y < 0 || x >= target.asset.intrinsicSize.w || y >= target.asset.intrinsicSize.h) return true;
|
|
const source = await createWandMask(target.asset.source, target.maskAsset?.source, Math.round(target.asset.intrinsicSize.w), Math.round(target.asset.intrinsicSize.h), x, y, { ...state.editor.tools.magicWand, mode: modeOverride ?? state.editor.tools.magicWand.mode });
|
|
if (target.maskAsset && target.maskLayer && (target.maskLayer.type === "image" || target.maskLayer.type === "raster")) {
|
|
store.dispatch(commandIds.documentApplyLayerMaskOperation, { maskLayerId: target.maskLayer.id, source, mimeType: "image/png", operation: { type: "magicWand" } });
|
|
return true;
|
|
}
|
|
const assetId = crypto.randomUUID();
|
|
const maskLayerId = crypto.randomUUID();
|
|
const width = Math.max(1, Math.round(target.asset.intrinsicSize.w));
|
|
const height = Math.max(1, Math.round(target.asset.intrinsicSize.h));
|
|
store.dispatch(commandIds.documentAddLayerMask, {
|
|
layerId: target.layer.id,
|
|
asset: { id: assetId, name: `${target.layer.name} Wand Mask`, mimeType: "image/png", source, intrinsicSize: { w: width, h: height } },
|
|
maskLayer: { id: maskLayerId, type: "raster", name: `${target.layer.name} Wand Mask`, visible: true, locked: false, opacity: 1, assetId, transform: { position: { x: target.bounds.x, y: target.bounds.y }, scale: { x: target.bounds.w / width, y: target.bounds.h / height }, rotation: target.layer.transform.rotation } },
|
|
});
|
|
store.dispatch(commandIds.toolExitMaskEdit, undefined);
|
|
store.dispatch(commandIds.toolSetActive, { tool: "magicWand" });
|
|
return true;
|
|
}
|
|
|
|
function resolveTarget(document: ImageDocument, editor: EditorState) {
|
|
const layerId = editor.selection.layerIds[0];
|
|
if (!layerId || editor.selection.layerIds.length !== 1) return undefined;
|
|
const layer = findLayer(document.artboards.flatMap((artboard) => artboard.layers), layerId);
|
|
if (!layer || (layer.type !== "image" && layer.type !== "raster")) return undefined;
|
|
const asset = document.assets.find((candidate) => candidate.id === layer.assetId);
|
|
const bounds = resolveTransformTargetBounds(document, { type: "layer", id: layer.id });
|
|
const layerMask = getLayerMask(layer);
|
|
const maskLayer = layerMask?.enabled ? findLayer(document.artboards.flatMap((artboard) => artboard.layers), layerMask.maskLayerId) : undefined;
|
|
const maskAsset = maskLayer && (maskLayer.type === "image" || maskLayer.type === "raster") ? document.assets.find((candidate) => candidate.id === maskLayer.assetId) : undefined;
|
|
return asset && bounds ? { layer, asset, bounds, maskLayer, maskAsset } : undefined;
|
|
}
|
|
|
|
function findLayer(layers: readonly Layer[], layerId: string): Layer | undefined {
|
|
for (const layer of layers) {
|
|
if (layer.id === layerId) return layer;
|
|
if (layer.type === "group") {
|
|
const found = findLayer(layer.children, layerId);
|
|
if (found) return found;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|