Skip to content

Commit

Permalink
demo: use a wasm-compiled graphviz -> SVG render
Browse files Browse the repository at this point in the history
  • Loading branch information
cormacrelf committed Dec 14, 2021
1 parent d02baac commit 90c5c69
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 647 deletions.
67 changes: 59 additions & 8 deletions crates/proc/src/disamb/finite_automata.rs
Expand Up @@ -11,7 +11,6 @@ use petgraph::graph::{Graph, NodeIndex};
use petgraph::visit::EdgeRef;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::fmt::Debug;

// XXX(pandoc): maybe force this to be a string and coerce pandoc output into a string
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -150,24 +149,76 @@ enum DebugNode {
StartAndAccepting,
}

impl fmt::Display for DebugNode {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Node => f.write_str(""),
_ => <Self as fmt::Debug>::fmt(self, f),
}
}
}

impl fmt::Display for EdgeData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Output(o) => <str as fmt::Debug>::fmt(&o, f),
_ => <Self as fmt::Debug>::fmt(self, f),
}
}
}

impl Dfa {
pub fn debug_graph(&self, _db: &dyn IrDatabase) -> String {
let g = self.graph.map(
|node, _| {
let cont = self.accepting.contains(&node);
if node == self.start && cont {
let start = node == self.start;
let accept = self.accepting.contains(&node);
if start && accept {
DebugNode::StartAndAccepting
} else if node == self.start {
} else if start {
DebugNode::Start
} else if cont {
} else if accept {
DebugNode::Accepting
} else {
DebugNode::Node
}
},
|_, edge| edge,
|_, edge| edge.clone(),
);
format!("{:?}", Dot::with_config(&g, &[]))
format!(
"{}",
Dot::with_attr_getters(
&g,
&[],
&|g, e| {
match &g[e.id()] {
EdgeData::Output(o) => {
return if o.starts_with(" ") || o.ends_with(" ") {
format!("label=<&quot;{}&quot;>", o)
} else {
format!("label=<{}>", o)
}
}
_ => r##"fontname="monospace" style="dashed" fontsize="12.0""##,
}
.to_string()
},
&|g, (node, _)| {
match &g[node] {
DebugNode::Start => {
r##"shape="invhouse" style="filled" fillcolor="#b6d6e2""##
}
DebugNode::Node => {
r##"shape="circle" width="0.2" style=filled fillcolor="#e2ecdf""##
}
DebugNode::Accepting => r##"shape="box" style=filled fillcolor="#A2B29F""##,
// reddish, because it's basically an error
DebugNode::StartAndAccepting => r##"style=filled fillcolor="#efb8a5""##,
}
.to_string()
}
)
)
}
}

Expand Down Expand Up @@ -231,7 +282,7 @@ impl Nfa {

use std::fmt::{self, Formatter};

impl Debug for Dfa {
impl fmt::Debug for Dfa {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
Expand Down
1 change: 0 additions & 1 deletion crates/wasm/js-demo/js/GraphViz.css
Expand Up @@ -17,7 +17,6 @@ g.type-BuiltIn .edgeLabel {

text {
font-weight: 300;
font-size: 14px;
}

.node rect {
Expand Down
64 changes: 10 additions & 54 deletions crates/wasm/js-demo/js/GraphViz.tsx
Expand Up @@ -2,70 +2,26 @@ import React, { useState, useCallback, useEffect, useRef } from 'react';
import { Driver, Reference } from '../../pkg';
import { Result } from 'safe-types';
import Select from 'react-select'
import dot from "graphlib-dot";
import dagreD3 from 'dagre-d3';
import { select } from 'd3-selection';
import { zoomIdentity } from 'd3-zoom';
import { curveBasis, curveBundle } from 'd3-shape';
import hpccWasm from '@hpcc-js/wasm';

import './GraphViz.css';

export const Dot = ({ dotString }: { dotString: string }) => {
if (dotString == null) return <></>;
const svgRef = useRef();
const svgGroupRef = useRef();
const svgRef = useRef<HTMLDivElement>();

let render = dagreD3.render();
const renderDot = async () => {
let svg = await hpccWasm.graphviz.layout(dotString, "svg", "dot", { wasmFolder: '/' })
if (svgRef.current != null) {
svgRef.current.innerHTML = svg;
}
};

useEffect(() => {
let svg = select(svgRef.current);
let group = select(svgGroupRef.current);

const g = dot.read(dotString);

g.nodes().forEach(v => {
var node = g.node(v);
// Round the corners of the nodes
node.rx = node.ry = 5;
let label: string = node.label;
if (label.match(/Accepting/)) {
node.class = "type-Accepting";
} else {
node.class = "";
}
});

g.edges().forEach(e => {
var edge = g.edge(e);
let label: string = edge.label;
if (label.match(/Output/)) {
} else {
edge.labelStyle = "font-weight: bold;"
}
// edge.curve = curveBasis;
edge.curve = curveBundle.beta(0.8);
});

render(group, g);

let cur = svgRef.current;
if (cur != null) {
let { height, width } = cur.getBBox();
let { height: gHeight, width: gWidth } = g.graph();
let transX = width - gWidth;
let transY = height - gHeight;
svg.attr("height", height);
svg.attr("width", width + 30);
group.attr("transform", zoomIdentity.translate(transX, transY))
}
return () => {
group.selectAll("*").remove();
}
renderDot();
}, [dotString, svgRef.current]);

return <svg className='dagre-d3' ref={svgRef} width={900} height={800}>
<g ref={svgGroupRef} />
</svg>;
return <div ref={svgRef}></div>;
}

export const GraphViz = ({ driver, references }: { driver: Result<Driver, any>, references: Reference[] }) => {
Expand Down
11 changes: 2 additions & 9 deletions crates/wasm/js-demo/package.json
Expand Up @@ -5,28 +5,21 @@
"repository": "https://github.com/rustwasm/rust-webpack-template",
"license": "(MIT OR Apache-2.0)",
"scripts": {
"copy": "rm -rf build && cp -R static build",
"copy": "rm -rf build && cp -R static build && cp node_modules/@hpcc-js/wasm/dist/graphvizlib.wasm build",
"index": "yarn run copy && sed s,%PUBLIC_URL%,$PUBLIC_URL,g < static/index.html > build/index.html",
"build": "yarn run index && yarn node build.js",
"serve": "yarn run index && yarn node serve.js"
},
"devDependencies": {
"@types/jest": "^27.0.3",
"@types/node": "^16.11.11",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"@wasm-tool/wasm-pack-plugin": "^1.6.0",
"esbuild": "^0.14.0",
"esbuild-plugin-wasm": "^1.0.0",
"typescript": "^4.5.2"
},
"dependencies": {
"d3-selection": "^3.0.0",
"d3-shape": "^3.0.1",
"d3-zoom": "^3.0.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
"graphlib-dot": "^0.6.4",
"@hpcc-js/wasm": "^1.12.6",
"immer": "^9.0.7",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down

0 comments on commit 90c5c69

Please sign in to comment.