seir_graph = {
let graph = d3.create('div').style('width', `${width}px`);
let source_code = `digraph {
S [pos="0,0!"]
E [pos="2.7,0!"]
I_1 [pos="4,0!"]
I_2 [pos="6,0.5!"]
I_3 [pos="8,0!"]
D [pos="10,0!"]
R [pos="6,-1.5!"]
S -> E [label="\\\\beta_1 I_1 S + \\\\beta_2 I_2 S + \\\\beta_3 I_3"]
E -> I_1 [label="\\\\alpha E"]
I_1 -> I_2 [label="p_1 I_1"]
I_2 -> I_3 [label="p_2 I_2"]
I_3-> D [label="\\\\mu I_3"]
I_1 -> R [label="\\\\gamma_1 I_1"]
I_2 -> R [label="\\\\gamma_2 I_2"]
I_3 -> R [label="\\\\gamma_3 I_3"]
}`;
d3.graphviz(graph.node())
.width(width)
.fit(true)
.zoom(false)
.engine('neato')
.renderDot(source_code);
let main_group = graph.select('g');
// Don't really want the title
main_group.select('title').remove();
// Typeset the nodes
main_group.selectAll('.node').each(function(e, i) {
let text = d3.select(this).select('text');
if (text.node() != null) {
let x = parseFloat(text.attr('x'));
let y = parseFloat(text.attr('y'));
let tex_group = main_group
.append('g')
.attr('transform', `translate(${x - 8} ${y - 10})`)
.append(() =>
MathJax.tex2svg(String.raw`${text.text()}`).querySelector("svg")
);
text.remove();
}
});
// Placement of the typeset edge labels is a bit trickier. The following
// list of shifts adjusts the placement of the labels from the location
// specified by graphviz.
let shifts = [
[60, -30],
[-9, -27],
[-37, -15],
[10, 5],
[-20, -10],
[10, 0],
[-25, -12],
[44, 5]
];
main_group.selectAll('.edge').each(function(e, i) {
let text = d3.select(this).select('text');
if (text.node() != null) {
let x = parseFloat(text.attr('x'));
let y = parseFloat(text.attr('y'));
let tex_group = main_group
.append('g')
.attr(
'transform',
`translate(${x + shifts[i][0]} ${y + shifts[i][1]}) scale(0.75)`
)
.append(() =>
MathJax.tex2svg(String.raw`${text.text()}`).querySelector("svg")
);
text.remove();
}
});
// There's far more space to the left of the graph than I'd expect;
// I guess the reason is that the first, pre-shifted edge label extends
// quite far to the left. A hacky fix is to redefine the main transform
// to fit it a bit better. Unfortunately, this breaks zoom.
main_group.attr('transform', `translate(-130, 204) scale(1.15)`);
return graph.node();
}