import {getConnectedEdges, type Node, type NodeProps, Position} from '@xyflow/react'; import {useState} from "react"; import styles from '../../VisProg.module.css'; import {Toolbar} from '../components/NodeComponents.tsx'; import {MultiConnectionHandle, SingleConnectionHandle} from "../components/RuleBasedHandle.tsx"; import {allowOnlyConnectionsFromType} from "../HandleRules.ts"; import useFlowStore from "../VisProgStores.tsx"; import {BeliefGlobalReduce, noBeliefCycles, noMatchingLeftRightBelief} from "./BeliefGlobals.ts"; import switchStyles from './InferredBeliefNode.module.css'; /** * The default data structure for an InferredBelief node */ export type InferredBeliefNodeData = { label: string; droppable: boolean; inferredBelief: InferredBelief; hasReduce: boolean; }; /** * stores a boolean to represent the operator * and a left and right BeliefNode (can be both an inferred and a basic belief) * in the form of their corresponding id's */ export type InferredBelief = { left: string | undefined, operator: boolean, right: string | undefined, } export type InferredBeliefNode = Node; /** * This function is called whenever a connection is made with this node type as the target * @param _thisNode the node of this node type which function is called * @param _sourceNodeId the source of the received connection */ export function InferredBeliefConnectionTarget(_thisNode: Node, _sourceNodeId: string) { const data = _thisNode.data as InferredBeliefNodeData; if ((useFlowStore.getState().nodes.find((node) => node.id === _sourceNodeId && ['basic_belief', 'inferred_belief'].includes(node.type!))) ) { const connectedEdges = getConnectedEdges([_thisNode], useFlowStore.getState().edges); switch(connectedEdges.find(edge => edge.source === _sourceNodeId)?.targetHandle){ case 'beliefLeft': data.inferredBelief.left = _sourceNodeId; break; case 'beliefRight': data.inferredBelief.right = _sourceNodeId; break; } } } /** * This function is called whenever a connection is made with this node type as the source * @param _thisNode the node of this node type which function is called * @param _targetNodeId the target of the created connection */ export function InferredBeliefConnectionSource(_thisNode: Node, _targetNodeId: string) { // no additional connection logic exists yet } /** * This function is called whenever a connection is disconnected with this node type as the target * @param _thisNode the node of this node type which function is called * @param _sourceNodeId the source of the disconnected connection */ export function InferredBeliefDisconnectionTarget(_thisNode: Node, _sourceNodeId: string) { const data = _thisNode.data as InferredBeliefNodeData; if (_sourceNodeId === data.inferredBelief.left) data.inferredBelief.left = undefined; if (_sourceNodeId === data.inferredBelief.right) data.inferredBelief.right = undefined; } /** * This function is called whenever a connection is disconnected with this node type as the source * @param _thisNode the node of this node type which function is called * @param _targetNodeId the target of the diconnected connection */ export function InferredBeliefDisconnectionSource(_thisNode: Node, _targetNodeId: string) { // no additional connection logic exists yet } export const InferredBeliefTooltip = ` Combines two beliefs into a single belief using logical inference, the node can be toggled between using "AND" and "OR" mode for inference`; /** * Defines how an InferredBelief node should be rendered * @param {NodeProps} props - Node properties provided by React Flow, including `id` and `data`. * @returns The rendered InferredBeliefNode React element. (React.JSX.Element) */ export default function InferredBeliefNode(props: NodeProps) { const data = props.data; const { updateNodeData } = useFlowStore(); // start of as an AND operator, true: "AND", false: "OR" const [enforceAllBeliefs, setEnforceAllBeliefs] = useState(true); // used to toggle operator function onToggle() { const newOperator = !enforceAllBeliefs; // compute the new value setEnforceAllBeliefs(newOperator); updateNodeData(props.id, { ...data, inferredBelief: { ...data.inferredBelief, operator: enforceAllBeliefs, } }); } return ( <>
{/* The checkbox used to toggle the operator between 'AND' and 'OR' */} {/* outgoing connections */} {/* incoming connections */}
); }; /** * Reduces each BasicBelief, including its children down into its core data. * @param {Node} node - The BasicBelief node to reduce. * @param {Node[]} nodes - The list of all nodes in the current flow graph. * @returns A simplified object containing the node label and its list of BasicBeliefs. */ export function InferredBeliefReduce(node: Node, nodes: Node[]) { const data = node.data as InferredBeliefNodeData; const leftBelief = nodes.find((node) => node.id === data.inferredBelief.left); const rightBelief = nodes.find((node) => node.id === data.inferredBelief.right); if (!leftBelief) { throw new Error("No Left belief found")} if (!rightBelief) { throw new Error("No Right Belief found")} const result: Record = { id: node.id, left: BeliefGlobalReduce(leftBelief, nodes), operator: data.inferredBelief.operator ? "AND" : "OR", right: BeliefGlobalReduce(rightBelief, nodes), }; return result }