feat: added an inferred belief node to the editor #42
@@ -6,13 +6,14 @@ import {
|
|||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import { Toolbar } from '../components/NodeComponents.tsx';
|
import { Toolbar } from '../components/NodeComponents.tsx';
|
||||||
import styles from '../../VisProg.module.css';
|
|
||||||
import {type HandleRule, type RuleResult, ruleResult} from "../HandleRuleLogic.ts";
|
import {type HandleRule, type RuleResult, ruleResult} from "../HandleRuleLogic.ts";
|
||||||
import {BeliefReduce} from "./BeliefReduce.ts";
|
import {BeliefReduce} from "./BeliefReduce.ts";
|
||||||
import switchStyles from './InferredBeliefNode.module.css';
|
|
||||||
import {MultiConnectionHandle, SingleConnectionHandle} from "../components/RuleBasedHandle.tsx";
|
import {MultiConnectionHandle, SingleConnectionHandle} from "../components/RuleBasedHandle.tsx";
|
||||||
import {allowOnlyConnectionsFromType} from "../HandleRules.ts";
|
import {allowOnlyConnectionsFromType} from "../HandleRules.ts";
|
||||||
import useFlowStore from "../VisProgStores.tsx";
|
import useFlowStore from "../VisProgStores.tsx";
|
||||||
|
import styles from '../../VisProg.module.css';
|
||||||
|
import switchStyles from './InferredBeliefNode.module.css';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default data structure for an InferredBelief node
|
* The default data structure for an InferredBelief node
|
||||||
@@ -24,6 +25,11 @@ export type InferredBeliefNodeData = {
|
|||||||
hasReduce: boolean;
|
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
|
||||||
|
*/
|
||||||
type InferredBelief = {
|
type InferredBelief = {
|
||||||
left: string | undefined,
|
left: string | undefined,
|
||||||
operator: boolean,
|
operator: boolean,
|
||||||
@@ -82,17 +88,28 @@ export function InferredBeliefDisconnectionSource(_thisNode: Node, _targetNodeId
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This rule makes it impossible to connect Inferred belief nodes
|
* makes it impossible to connect Inferred belief nodes
|
||||||
* if the connection would create a looping connection between inferred beliefs
|
* if the connection would create a cyclical connection between inferred beliefs
|
||||||
*/
|
*/
|
||||||
const noBeliefCycles : HandleRule = (connection, _): RuleResult => {
|
const noBeliefCycles : HandleRule = (connection, _): RuleResult => {
|
||||||
const { nodes, edges } = useFlowStore.getState();
|
const { nodes, edges } = useFlowStore.getState();
|
||||||
|
const defaultErrorMessage = "Cyclical connection exists between inferred beliefs";
|
||||||
|
|
||||||
function checkForCycle(targetNodeId: string, current: string) : RuleResult {
|
/**
|
||||||
const outgoingBeliefs = getOutgoers({id: current}, nodes, edges).filter(node => node.type === 'inferred_belief');
|
* recursively checks for cyclical connections between InferredBelief nodes
|
||||||
|
*
|
||||||
|
* to check for a cycle provide the source of an attempted connection as the targetNode for the cycle check,
|
||||||
|
* the currentNodeId should be initialised with the id of the targetNode of the attempted connection.
|
||||||
|
*
|
||||||
|
* @param {string} targetNodeId - the id of the node we are looking for as the endpoint of a cyclical connection
|
||||||
|
* @param {string} currentNodeId - the id of the node we are checking for outgoing connections to the provided target node
|
||||||
|
* @returns {RuleResult}
|
||||||
|
*/
|
||||||
|
function checkForCycle(targetNodeId: string, currentNodeId: string) : RuleResult {
|
||||||
|
const outgoingBeliefs = getOutgoers({id: currentNodeId}, nodes, edges).filter(node => node.type === 'inferred_belief');
|
||||||
if (outgoingBeliefs.length === 0) return ruleResult.satisfied;
|
if (outgoingBeliefs.length === 0) return ruleResult.satisfied;
|
||||||
if (outgoingBeliefs.some(node => node.id === targetNodeId)) return ruleResult
|
if (outgoingBeliefs.some(node => node.id === targetNodeId)) return ruleResult
|
||||||
.notSatisfied("Cyclical connection exists between inferred beliefs");
|
.notSatisfied(defaultErrorMessage);
|
||||||
|
|
||||||
const next = outgoingBeliefs.map(node => checkForCycle(targetNodeId, node.id)).find(result => !result.isSatisfied);
|
const next = outgoingBeliefs.map(node => checkForCycle(targetNodeId, node.id)).find(result => !result.isSatisfied);
|
||||||
return next
|
return next
|
||||||
@@ -101,15 +118,15 @@ const noBeliefCycles : HandleRule = (connection, _): RuleResult => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return connection.source === connection.target
|
return connection.source === connection.target
|
||||||
? ruleResult.notSatisfied("Cyclical connection exists between inferred beliefs")
|
? ruleResult.notSatisfied(defaultErrorMessage)
|
||||||
: checkForCycle(connection.source, connection.target);
|
: checkForCycle(connection.source, connection.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines how a BasicBelief node should be rendered
|
* Defines how an InferredBelief node should be rendered
|
||||||
* @param props - Node properties provided by React Flow, including `id` and `data`.
|
* @param {NodeProps<InferredBeliefNode>} props - Node properties provided by React Flow, including `id` and `data`.
|
||||||
* @returns The rendered BasicBeliefNode React element (React.JSX.Element).
|
* @returns The rendered InferredBeliefNode React element. (React.JSX.Element)
|
||||||
*/
|
*/
|
||||||
export default function InferredBeliefNode(props: NodeProps<InferredBeliefNode>) {
|
export default function InferredBeliefNode(props: NodeProps<InferredBeliefNode>) {
|
||||||
const data = props.data;
|
const data = props.data;
|
||||||
@@ -117,6 +134,7 @@ export default function InferredBeliefNode(props: NodeProps<InferredBeliefNode>)
|
|||||||
// start of as an AND operator, true: "AND", false: "OR"
|
// start of as an AND operator, true: "AND", false: "OR"
|
||||||
const [enforceAllBeliefs, setEnforceAllBeliefs] = useState(true);
|
const [enforceAllBeliefs, setEnforceAllBeliefs] = useState(true);
|
||||||
|
|
||||||
|
// used to toggle operator
|
||||||
function onToggle() {
|
function onToggle() {
|
||||||
setEnforceAllBeliefs(!enforceAllBeliefs);
|
setEnforceAllBeliefs(!enforceAllBeliefs);
|
||||||
|
|
||||||
@@ -128,11 +146,12 @@ export default function InferredBeliefNode(props: NodeProps<InferredBeliefNode>)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Toolbar nodeId={props.id} allowDelete={true}/>
|
<Toolbar nodeId={props.id} allowDelete={true}/>
|
||||||
<div className={`${styles.defaultNode} ${styles.nodeInferredBelief}`}>
|
<div className={`${styles.defaultNode} ${styles.nodeInferredBelief}`}>
|
||||||
|
{/* The checkbox used to toggle the operator between 'AND' and 'OR' */}
|
||||||
<label className={switchStyles.operatorSwitch}>
|
<label className={switchStyles.operatorSwitch}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -169,8 +188,8 @@ export default function InferredBeliefNode(props: NodeProps<InferredBeliefNode>)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduces each BasicBelief, including its children down into its core data.
|
* Reduces each BasicBelief, including its children down into its core data.
|
||||||
* @param node - The BasicBelief node to reduce.
|
* @param {Node} node - The BasicBelief node to reduce.
|
||||||
* @param nodes - The list of all nodes in the current flow graph.
|
* @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.
|
* @returns A simplified object containing the node label and its list of BasicBeliefs.
|
||||||
*/
|
*/
|
||||||
export function InferredBeliefReduce(node: Node, nodes: Node[]) {
|
export function InferredBeliefReduce(node: Node, nodes: Node[]) {
|
||||||
@@ -187,5 +206,6 @@ export function InferredBeliefReduce(node: Node, nodes: Node[]) {
|
|||||||
operator: data.inferredBelief.operator ? "AND" : "OR",
|
operator: data.inferredBelief.operator ? "AND" : "OR",
|
||||||
right: BeliefReduce(rightBelief, nodes),
|
right: BeliefReduce(rightBelief, nodes),
|
||||||
};
|
};
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user