mirror of
https://github.com/ellmau/adf-obdd.git
synced 2025-12-20 09:39:38 +01:00
Finish basic visualization of solve response
This commit is contained in:
parent
cdd21f7e4f
commit
dbde6b86f4
@ -1,11 +1,13 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
const { useState, useCallback } = React;
|
|
||||||
|
|
||||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||||
import { Backdrop, Button, CircularProgress, CssBaseline, Container, Link, Typography, TextField } from '@mui/material';
|
import {
|
||||||
|
Backdrop, Button, CircularProgress, CssBaseline, Container, Link, Paper, Typography, TextField,
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
//import Graph from './graph.tsx';
|
import Graph from './graph.tsx';
|
||||||
|
|
||||||
|
const { useState, useCallback } = React;
|
||||||
|
|
||||||
const darkTheme = createTheme({
|
const darkTheme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
@ -49,8 +51,8 @@ function App() {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ code }),
|
body: JSON.stringify({ code }),
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then((res) => res.json())
|
||||||
.then(data => setGraph(data))
|
.then((data) => setGraph(data))
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
// TODO: error handling
|
// TODO: error handling
|
||||||
},
|
},
|
||||||
@ -59,36 +61,53 @@ function App() {
|
|||||||
|
|
||||||
console.log(graph);
|
console.log(graph);
|
||||||
|
|
||||||
return <ThemeProvider theme={darkTheme}>
|
return (
|
||||||
<CssBaseline/>
|
<ThemeProvider theme={darkTheme}>
|
||||||
<main>
|
<CssBaseline />
|
||||||
<Typography variant="h2" component="h1" align="center" gutterBottom>
|
<main>
|
||||||
Solve your ADF Problem with Style!
|
<Typography variant="h2" component="h1" align="center" gutterBottom>
|
||||||
</Typography>
|
Solve your ADF Problem with Style!
|
||||||
|
</Typography>
|
||||||
|
|
||||||
<Container>
|
<Container>
|
||||||
<TextField
|
<TextField
|
||||||
name="code"
|
name="code"
|
||||||
label="Put your code here:"
|
label="Put your code here:"
|
||||||
helperText={<>For more info on the syntax, have a look <Link href="https://github.com/ellmau/adf-obdd" target="_blank" rel="noreferrer">here</Link>.</>}
|
helperText={(
|
||||||
multiline
|
<>
|
||||||
fullWidth
|
For more info on the syntax, have a look
|
||||||
variant="filled"
|
<Link href="https://github.com/ellmau/adf-obdd" target="_blank" rel="noreferrer">here</Link>
|
||||||
value={code}
|
.
|
||||||
onChange={(event) => { setCode(event.target.value); }}
|
</>
|
||||||
/>
|
)}
|
||||||
</Container>
|
multiline
|
||||||
<Container maxWidth="xs">
|
fullWidth
|
||||||
<Button fullWidth variant="outlined" onClick={submitHandler}>Solve it!</Button>
|
variant="filled"
|
||||||
</Container>
|
value={code}
|
||||||
</main>
|
onChange={(event) => { setCode(event.target.value); }}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
<Container maxWidth="xs">
|
||||||
|
<Button fullWidth variant="outlined" onClick={submitHandler}>Solve it!</Button>
|
||||||
|
</Container>
|
||||||
|
|
||||||
<Backdrop
|
{graph
|
||||||
open={loading}
|
&& (
|
||||||
>
|
<Container>
|
||||||
<CircularProgress color="inherit" />
|
<Paper elevation={3} square sx={{ marginTop: 4, marginBottom: 4 }}>
|
||||||
</Backdrop>
|
<Graph graph={graph} />
|
||||||
</ThemeProvider>;
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<Backdrop
|
||||||
|
open={loading}
|
||||||
|
>
|
||||||
|
<CircularProgress color="inherit" />
|
||||||
|
</Backdrop>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@ -1,27 +1,50 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
import G6 from '@antv/g6';
|
import G6 from '@antv/g6';
|
||||||
import testData from '../test-data.ts';
|
|
||||||
|
|
||||||
function Graph() {
|
interface Props {
|
||||||
|
graph: {
|
||||||
|
lo_edges: [number, number][],
|
||||||
|
hi_edges: [number, number][],
|
||||||
|
node_labels: { [key: number]: string },
|
||||||
|
tree_root_labels: { [key: number]: string[] },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Graph(props: Props) {
|
||||||
|
const { graph: graphProps } = props;
|
||||||
|
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
|
|
||||||
let graph = null;
|
const graphRef = useRef();
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
if (!graph) {
|
if (!graphRef.current) {
|
||||||
graph = new G6.Graph({
|
graphRef.current = new G6.Graph({
|
||||||
container: ref.current,
|
container: ref.current,
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 800,
|
height: 600,
|
||||||
|
fitView: true,
|
||||||
|
rankdir: 'TB',
|
||||||
|
align: 'DR',
|
||||||
|
nodesep: 100,
|
||||||
|
ranksep: 100,
|
||||||
modes: {
|
modes: {
|
||||||
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
|
default: ['drag-canvas', 'zoom-canvas', 'drag-node'],
|
||||||
},
|
},
|
||||||
layout: { type: 'dagre' },
|
layout: { type: 'dagre' },
|
||||||
defaultNode: {
|
defaultNode: {
|
||||||
|
anchorPoints: [[0.5, 0], [0, 0.5], [1, 0.5], [0.5, 1]],
|
||||||
|
type: 'rect',
|
||||||
style: {
|
style: {
|
||||||
r: 20,
|
radius: 2,
|
||||||
|
},
|
||||||
|
labelCfg: {
|
||||||
|
style: {
|
||||||
|
// fontWeight: 700,
|
||||||
|
fontFamily: 'Roboto',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultEdge: {
|
defaultEdge: {
|
||||||
@ -49,6 +72,8 @@ function Graph() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const graph = graphRef.current;
|
||||||
|
|
||||||
// Mouse enter a node
|
// Mouse enter a node
|
||||||
graph.on('node:mouseenter', (e) => {
|
graph.on('node:mouseenter', (e) => {
|
||||||
const nodeItem = e.item; // Get the target item
|
const nodeItem = e.item; // Get the target item
|
||||||
@ -63,12 +88,31 @@ function Graph() {
|
|||||||
|
|
||||||
// Click a node
|
// Click a node
|
||||||
graph.on('node:click', (e) => {
|
graph.on('node:click', (e) => {
|
||||||
// Swich the 'click' state of the node to be false
|
const nodeItem = e.item; // et the clicked item
|
||||||
|
|
||||||
|
let onlyRemoveStates = false;
|
||||||
|
if (nodeItem.hasState('highlight')) {
|
||||||
|
onlyRemoveStates = true;
|
||||||
|
}
|
||||||
|
|
||||||
const clickNodes = graph.findAllByState('node', 'highlight');
|
const clickNodes = graph.findAllByState('node', 'highlight');
|
||||||
clickNodes.forEach((cn) => {
|
clickNodes.forEach((cn) => {
|
||||||
graph.setItemState(cn, 'highlight', false);
|
graph.setItemState(cn, 'highlight', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const lowlightNodes = graph.findAllByState('node', 'lowlight');
|
||||||
|
lowlightNodes.forEach((cn) => {
|
||||||
|
graph.setItemState(cn, 'lowlight', false);
|
||||||
|
});
|
||||||
|
const lowlightEdges = graph.findAllByState('edge', 'lowlight');
|
||||||
|
lowlightEdges.forEach((cn) => {
|
||||||
|
graph.setItemState(cn, 'lowlight', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (onlyRemoveStates) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
graph.getNodes().forEach((node) => {
|
graph.getNodes().forEach((node) => {
|
||||||
graph.setItemState(node, 'lowlight', true);
|
graph.setItemState(node, 'lowlight', true);
|
||||||
});
|
});
|
||||||
@ -76,24 +120,98 @@ function Graph() {
|
|||||||
graph.setItemState(edge, 'lowlight', true);
|
graph.setItemState(edge, 'lowlight', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
const nodeItem = e.item; // et the clicked item
|
const relevantNodeIds = [];
|
||||||
graph.setItemState(nodeItem, 'lowlight', false);
|
const relevantLoEdges = [];
|
||||||
graph.setItemState(nodeItem, 'highlight', true);
|
const relevantHiEdges = [];
|
||||||
nodeItem.getEdges().forEach((edge) => {
|
let newNodeIds = [nodeItem.getModel().id];
|
||||||
graph.setItemState(edge, 'lowlight', false);
|
let newLoEdges = [];
|
||||||
});
|
let newHiEdges = [];
|
||||||
});
|
|
||||||
|
|
||||||
graph.data({
|
while (newNodeIds.length > 0 || newLoEdges.length > 0 || newHiEdges.length > 0) {
|
||||||
nodes: testData.map((node, index) => ({ id: index.toString(), label: node.label })),
|
relevantNodeIds.push(...newNodeIds);
|
||||||
edges: testData.flatMap((node, index) => [{ source: index.toString(), target: node.lo.toString(), style: { stroke: 'red', lineWidth: 2 } }, { source: index.toString(), target: node.hi.toString(), style: { stroke: 'green', lineWidth: 2 } }]),
|
relevantLoEdges.push(...newLoEdges);
|
||||||
|
relevantHiEdges.push(...newHiEdges);
|
||||||
|
|
||||||
|
newLoEdges = graphProps.lo_edges
|
||||||
|
.filter((edge) => relevantNodeIds.includes(edge[0].toString())
|
||||||
|
&& !relevantLoEdges.includes(edge));
|
||||||
|
newHiEdges = graphProps.hi_edges
|
||||||
|
.filter((edge) => relevantNodeIds.includes(edge[0].toString())
|
||||||
|
&& !relevantHiEdges.includes(edge));
|
||||||
|
|
||||||
|
newNodeIds = newLoEdges
|
||||||
|
.concat(newHiEdges)
|
||||||
|
.map((edge) => edge[1].toString())
|
||||||
|
.filter((id) => !relevantNodeIds.includes(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
const relevantEdgeIds = relevantLoEdges
|
||||||
|
.map(([source, target]) => `LO_${source}_${target}`)
|
||||||
|
.concat(
|
||||||
|
relevantHiEdges
|
||||||
|
.map(([source, target]) => `HI_${source}_${target}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(relevantNodeIds);
|
||||||
|
console.log(relevantEdgeIds);
|
||||||
|
|
||||||
|
relevantNodeIds
|
||||||
|
.forEach((id) => {
|
||||||
|
graph.setItemState(id, 'lowlight', false);
|
||||||
|
graph.setItemState(id, 'highlight', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
relevantEdgeIds
|
||||||
|
.forEach((id) => {
|
||||||
|
graph.setItemState(id, 'lowlight', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// graph.setItemState(nodeItem, 'lowlight', false);
|
||||||
|
// graph.setItemState(nodeItem, 'highlight', true);
|
||||||
|
// nodeItem.getEdges().forEach((edge) => {
|
||||||
|
// graph.setItemState(edge, 'lowlight', false);
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
graph.render();
|
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <div ref={ref} />;
|
useEffect(
|
||||||
|
() => {
|
||||||
|
const graph = graphRef.current;
|
||||||
|
|
||||||
|
const nodes = Object.keys(graphProps.node_labels).map((id) => {
|
||||||
|
const mainLabel = graphProps.node_labels[id];
|
||||||
|
const subLabel = graphProps.tree_root_labels[id].length > 0 ? `Root for: ${graphProps.tree_root_labels[id].join(' ; ')}` : '';
|
||||||
|
|
||||||
|
const label = subLabel.length > 0 ? `${mainLabel}\n${subLabel}` : mainLabel;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: id.toString(),
|
||||||
|
label,
|
||||||
|
style: {
|
||||||
|
height: subLabel.length > 0 ? 60 : 30,
|
||||||
|
width: Math.max(30, 10 * mainLabel.length, 10 * subLabel.length),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const edges = graphProps.lo_edges.map(([source, target]) => ({
|
||||||
|
id: `LO_${source}_${target}`, source: source.toString(), target: target.toString(), style: { stroke: 'red', lineWidth: 2 },
|
||||||
|
}))
|
||||||
|
.concat(graphProps.hi_edges.map(([source, target]) => ({
|
||||||
|
id: `HI_${source}_${target}`, source: source.toString(), target: target.toString(), style: { stroke: 'green', lineWidth: 2 },
|
||||||
|
})));
|
||||||
|
|
||||||
|
graph.data({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
});
|
||||||
|
graph.render();
|
||||||
|
},
|
||||||
|
[graphProps],
|
||||||
|
);
|
||||||
|
|
||||||
|
return <div ref={ref} style={{ overflow: 'hidden' }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Graph;
|
export default Graph;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user