Skip to content

Commit

Permalink
Unified generic output (#2911)
Browse files Browse the repository at this point in the history
* Unified generic output

* Explain condition

* More efficient check
  • Loading branch information
RunDevelopment authored May 24, 2024
1 parent 4854e59 commit e340d0d
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 123 deletions.
2 changes: 1 addition & 1 deletion backend/src/api/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from .types import InputId, OutputId

OutputKind = Literal["image", "large-image", "tagged", "generic"]
OutputKind = Literal["large-image", "tagged", "generic"]
BroadcastData = Mapping[str, object]

T = TypeVar("T")
Expand Down
2 changes: 1 addition & 1 deletion backend/src/nodes/properties/outputs/numpy_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __init__(
self,
label: str = "Image",
image_type: navi.ExpressionJson = "Image",
kind: OutputKind = "image",
kind: OutputKind = "generic",
has_handle: bool = True,
channels: int | None = None,
shape_as: int | InputId | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/common/common-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export type Input =
| ColorInput
| StaticValueInput;

export type OutputKind = 'image' | 'large-image' | 'tagged' | 'generic';
export type OutputKind = 'large-image' | 'tagged' | 'generic';

export interface Output {
readonly id: OutputId;
Expand Down
3 changes: 0 additions & 3 deletions src/renderer/components/node/NodeOutputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { BackendContext } from '../../contexts/BackendContext';
import { GlobalContext, GlobalVolatileContext } from '../../contexts/GlobalNodeState';
import { NodeState } from '../../helpers/nodeState';
import { useIsCollapsedNode } from '../../hooks/useIsCollapsedNode';
import { DefaultImageOutput } from '../outputs/DefaultImageOutput';
import { GenericOutput } from '../outputs/GenericOutput';
import { LargeImageOutput } from '../outputs/LargeImageOutput';
import { OutputContainer } from '../outputs/OutputContainer';
Expand All @@ -20,13 +19,11 @@ import { TaggedOutput } from '../outputs/TaggedOutput';
const OutputComponents: Readonly<
Record<OutputKind, React.MemoExoticComponent<(props: OutputProps) => JSX.Element>>
> = {
image: DefaultImageOutput,
'large-image': LargeImageOutput,
tagged: TaggedOutput,
generic: GenericOutput,
};
const OutputIsGeneric: Readonly<Record<OutputKind, boolean>> = {
image: true,
'large-image': false,
tagged: false,
generic: true,
Expand Down
115 changes: 0 additions & 115 deletions src/renderer/components/outputs/DefaultImageOutput.tsx

This file was deleted.

125 changes: 123 additions & 2 deletions src/renderer/components/outputs/GenericOutput.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,123 @@
import { Flex, Spacer, Text } from '@chakra-ui/react';
import { Type, getStructDescriptor, isDisjointWith, isSubsetOf } from '@chainner/navi';
import { Center, Flex, Icon, Spacer, Text } from '@chakra-ui/react';
import { memo } from 'react';
import { BsEyeFill } from 'react-icons/bs';
import { useReactFlow } from 'reactflow';
import { useContext } from 'use-context-selector';
import {
EdgeData,
InputId,
NodeData,
NodeSchema,
Output,
SchemaId,
} from '../../../common/common-types';
import { getChainnerScope } from '../../../common/types/chainner-scope';
import { createUniqueId, lazy, lazyKeyed, stringifySourceHandle } from '../../../common/util';
import { FakeNodeContext } from '../../contexts/FakeExampleContext';
import { GlobalContext } from '../../contexts/GlobalNodeState';
import { TypeTags } from '../TypeTag';
import { OutputProps } from './props';

export const GenericOutput = memo(({ output, type }: OutputProps) => {
const VIEW_SCHEMA_ID = 'chainner:image:view' as SchemaId;

interface ViewImageButtonProps {
output: Output;
id: string;
schema: NodeSchema;
}
const ViewImageButton = memo(({ output, id, schema }: ViewImageButtonProps) => {
const { selectNode, createNode, createEdge } = useContext(GlobalContext);
const { getNodes, getEdges } = useReactFlow<NodeData, EdgeData>();
const { isFake } = useContext(FakeNodeContext);

if (isFake) return null;

return (
<Center
_hover={{
backgroundColor: 'var(--node-image-preview-button-bg-hover)',
}}
bgColor="var(--node-image-preview-button-bg)"
borderRadius="md"
className="nodrag"
cursor="pointer"
h="1.75rem"
maxH="1.75rem"
maxW="1.75rem"
minH="1.75rem"
minW="1.75rem"
my="0.125rem"
overflow="hidden"
transition="0.15s ease-in-out"
w="1.75rem"
onClick={() => {
const byId = new Map(getNodes().map((n) => [n.id, n]));

const sourceHandle = stringifySourceHandle({ nodeId: id, outputId: output.id });

// check whether there already is a view node
const viewId = getEdges()
.filter((e) => e.source === id && e.sourceHandle === sourceHandle)
.map((e) => e.target)
.find((i) => byId.get(i)?.data.schemaId === VIEW_SCHEMA_ID);
if (viewId !== undefined) {
// select view node
selectNode(viewId);
return;
}

const containingNode = byId.get(id);
if (containingNode) {
const nodeId = createUniqueId();
const outputIndex = schema.outputs.findIndex((o) => o.id === output.id);

// TODO: This is a bit of hardcoding, but it works
createNode({
id: nodeId,
position: {
x:
containingNode.position.x +
(containingNode.width ?? 0) +
75 +
outputIndex * 20,
y: containingNode.position.y + outputIndex * 30,
},
data: {
schemaId: VIEW_SCHEMA_ID,
},
});
createEdge(
{ nodeId: id, outputId: output.id },
{ nodeId, inputId: 0 as InputId }
);
}
}}
>
<Icon
as={BsEyeFill}
color="var(--node-image-preview-button-fg)"
/>
</Center>
);
});

const getImageType = lazy(() => getStructDescriptor(getChainnerScope(), 'Image').default);
const isImageDefinition = lazyKeyed((type: Type) => {
if (isDisjointWith(type, getImageType())) {
return false;
}
if (isSubsetOf(type, getImageType())) {
return true;
}
return null;
});
const isImage = lazyKeyed((type: Type) => isSubsetOf(type, getImageType()));

export const GenericOutput = memo(({ output, type, id, schema, definitionType }: OutputProps) => {
// We first check the definition type first, because it changes less, which makes it more efficient to cache.
const viewImage = isImageDefinition(definitionType) ?? isImage(type);

return (
<Flex
alignItems="center"
Expand All @@ -12,6 +126,13 @@ export const GenericOutput = memo(({ output, type }: OutputProps) => {
verticalAlign="middle"
w="full"
>
{viewImage && (
<ViewImageButton
id={id}
output={output}
schema={schema}
/>
)}
<Spacer />
<TypeTags
longText
Expand Down

0 comments on commit e340d0d

Please sign in to comment.