mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-15 06:45:21 -05:00
73 lines
3.0 KiB
TypeScript
73 lines
3.0 KiB
TypeScript
import { useStore } from '@nanostores/react';
|
|
import { useAppSelector } from 'app/store/storeHooks';
|
|
import { SyncableMap } from 'common/util/SyncableMap/SyncableMap';
|
|
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
|
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
|
|
import { useEntityAdapterSafe } from 'features/controlLayers/hooks/useEntityAdapter';
|
|
import type { AnyObjectRenderer } from '../konva/CanvasObject/types';
|
|
import { getEmptyRect } from 'features/controlLayers/konva/util';
|
|
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
|
import type { CanvasEntityIdentifier, Rect } from 'features/controlLayers/store/types';
|
|
import { isTransformableEntityIdentifier } from 'features/controlLayers/store/types';
|
|
import { atom } from 'nanostores';
|
|
import { useCallback, useMemo, useSyncExternalStore } from 'react';
|
|
|
|
// When the entity is empty (the rect has no size) or there are no renderers, we have nothing to transform. Because the
|
|
// entity is dynamic, and we need reactivity on these values, we need to do a little hack. These fallback objects
|
|
// can be used to provide a default value for the useStore and useSyncExternalStore hooks, which require _some_ value
|
|
// to be used.
|
|
const $fallbackPixelRect = atom<Rect>(getEmptyRect());
|
|
const fallbackRenderersMap = new SyncableMap<string, AnyObjectRenderer>();
|
|
|
|
export const useEntityTransform = (entityIdentifier: CanvasEntityIdentifier | null) => {
|
|
const canvasManager = useCanvasManager();
|
|
const adapter = useEntityAdapterSafe(entityIdentifier);
|
|
const isStaging = useAppSelector(selectIsStaging);
|
|
const isBusy = useCanvasIsBusy();
|
|
// Use the fallback pixel rect if the adapter is not available
|
|
const pixelRect = useStore(adapter?.transformer.$pixelRect ?? $fallbackPixelRect);
|
|
// Use the fallback renderers map if the adapter is not available
|
|
const renderers = useSyncExternalStore(
|
|
adapter?.renderer.renderers.subscribe ?? fallbackRenderersMap.subscribe,
|
|
adapter?.renderer.renderers.getSnapshot ?? fallbackRenderersMap.getSnapshot
|
|
);
|
|
|
|
const start = useCallback(() => {
|
|
if (!entityIdentifier) {
|
|
return;
|
|
}
|
|
if (!isTransformableEntityIdentifier(entityIdentifier)) {
|
|
return;
|
|
}
|
|
const adapter = canvasManager.getAdapter(entityIdentifier);
|
|
if (!adapter) {
|
|
return;
|
|
}
|
|
adapter.transformer.startTransform();
|
|
}, [entityIdentifier, canvasManager]);
|
|
|
|
const isDisabled = useMemo(() => {
|
|
if (!entityIdentifier) {
|
|
return true;
|
|
}
|
|
if (!isTransformableEntityIdentifier(entityIdentifier)) {
|
|
return true;
|
|
}
|
|
if (!adapter) {
|
|
return true;
|
|
}
|
|
if (isBusy || isStaging) {
|
|
return true;
|
|
}
|
|
if (pixelRect.width === 0 || pixelRect.height === 0) {
|
|
return true;
|
|
}
|
|
if (renderers.size === 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}, [entityIdentifier, adapter, isBusy, isStaging, pixelRect.width, pixelRect.height, renderers.size]);
|
|
|
|
return { isDisabled, start } as const;
|
|
};
|