- Implemented ComfyGenerateRequest type and associated functions for generating images using various architectures and modes. - Added functions for listing generation options and handling image uploads. - Created workflows for different generation modes including SDXL, Z-Image, Z-Image Turbo, and Anima. - Introduced GenerationJobStatus component to display the status of ongoing generation jobs. - Developed MaskControls for managing mask operations and displaying mask analysis. - Created palette items for tool selection, layer management, and generation settings.
61 lines
3.8 KiB
TypeScript
61 lines
3.8 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 !== "group") {
|
|
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 === "group") 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 !== "group" ? 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;
|
|
}
|