Portal/rsconcept/frontend/src/features/rsform/models/graph-api.ts

119 lines
3.3 KiB
TypeScript
Raw Normal View History

2025-07-09 12:43:00 +03:00
/**
* Module: Graph of Terms graphical representation.
*/
import { type Edge, type Node } from 'reactflow';
import dagre from '@dagrejs/dagre';
import { PARAMETER } from '@/utils/constants';
import { CstType } from '../backend/types';
import { type GraphFilterParams } from '../stores/term-graph';
import { type IConstituenta, type IRSForm } from './rsform';
export interface TGNodeState {
cst: IConstituenta;
focused: boolean;
}
/** Represents graph node. */
export interface TGNodeData extends Node {
id: string;
data: TGNodeState;
}
/** Represents graph node internal data. */
export interface TGNodeInternal {
id: string;
data: TGNodeState;
selected: boolean;
dragging: boolean;
xPos: number;
yPos: number;
}
export function applyLayout(nodes: Node<TGNodeState>[], edges: Edge[], subLabels: boolean) {
const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
dagreGraph.setGraph({
rankdir: 'TB',
ranksep: subLabels ? 60 : 40,
nodesep: subLabels ? 100 : 20,
ranker: 'network-simplex'
});
nodes.forEach(node => {
dagreGraph.setNode(node.id, { width: 2 * PARAMETER.graphNodeRadius, height: 2 * PARAMETER.graphNodeRadius });
});
edges.forEach(edge => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
nodes.forEach(node => {
const nodeWithPosition = dagreGraph.node(node.id);
node.position.x = nodeWithPosition.x - PARAMETER.graphNodeRadius;
node.position.y = nodeWithPosition.y - PARAMETER.graphNodeRadius;
});
}
export function produceFilteredGraph(schema: IRSForm, params: GraphFilterParams, focusCst: IConstituenta | null) {
const filtered = schema.graph.clone();
const allowedTypes: CstType[] = (() => {
const result: CstType[] = [];
if (params.allowBase) result.push(CstType.BASE);
if (params.allowStruct) result.push(CstType.STRUCTURED);
if (params.allowTerm) result.push(CstType.TERM);
if (params.allowAxiom) result.push(CstType.AXIOM);
if (params.allowFunction) result.push(CstType.FUNCTION);
if (params.allowPredicate) result.push(CstType.PREDICATE);
if (params.allowConstant) result.push(CstType.CONSTANT);
if (params.allowTheorem) result.push(CstType.THEOREM);
2025-08-12 15:39:11 +03:00
if (params.allowNominal) result.push(CstType.NOMINAL);
2025-07-09 12:43:00 +03:00
return result;
})();
if (params.noHermits) {
filtered.removeIsolated();
}
if (params.noTemplates) {
schema.items.forEach(cst => {
if (cst !== focusCst && cst.is_template) {
filtered.foldNode(cst.id);
}
});
}
if (allowedTypes.length < Object.values(CstType).length) {
schema.items.forEach(cst => {
if (cst !== focusCst && !allowedTypes.includes(cst.cst_type)) {
filtered.foldNode(cst.id);
}
});
}
if (!focusCst && params.foldDerived) {
schema.items.forEach(cst => {
if (cst.spawner) {
filtered.foldNode(cst.id);
}
});
}
if (focusCst) {
const includes: number[] = [
focusCst.id,
...focusCst.spawn,
...(params.focusShowInputs ? schema.graph.expandInputs([focusCst.id]) : []),
...(params.focusShowOutputs ? schema.graph.expandOutputs([focusCst.id]) : [])
];
schema.items.forEach(cst => {
if (!includes.includes(cst.id)) {
filtered.foldNode(cst.id);
}
});
}
if (params.noTransitive) {
filtered.transitiveReduction();
}
return filtered;
}