Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scale and Center D3-Graphviz Graph

What is the best way to scale and center a graph using d3-graphviz? I was hopeful that I could use scale(0.5) but this leaves the resulting graph uncentered.

I could probably go in with an .attributer() and manually adjust the <svg> and <g> elements to get what I'm looking for, but I figured there was probably a better way?

d3.select("#graph")
  .graphviz()
    .width(300)
    .height(300)
    .fit(true)
    .scale(.5)
    .renderDot('digraph {a -> b}');
<script src="//d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/@hpcc-js/[email protected]/dist/index.min.js"></script>
<script src="https://unpkg.com/[email protected]/build/d3-graphviz.js"></script>
  
<div id="graph" style="width: 300px; height: 300px; border: 1px solid black"></div>
like image 386
tgordon18 Avatar asked May 20 '21 18:05

tgordon18


2 Answers

There's no simple built-in way, but you can achieve almost anything with the attributer like so:

const px2pt = 3 / 4;

function attributer(datum, index, nodes) {
    var selection = d3.select(this);
    if (datum.tag == "svg") {
        var width = datum.attributes.width;
        var height = datum.attributes.height;
        w = datum.attributes.viewBox.split(" ")[2];
        h = datum.attributes.viewBox.split(" ")[3];
        var x = (width * px2pt - w / 2) / 2;
        var y = (height * px2pt - h / 2) / 2;
        selection
            .attr("width", width)
            .attr("height", height)
            .attr("viewBox", -x + " " + -y + " " + (width * px2pt) + " " + (height * px2pt));
        datum.attributes.width = width;
        datum.attributes.height = height;
        datum.attributes.viewBox = -x + " " + -y + " " + (width * px2pt) + " " + (height * px2pt);
    }
}

d3.select("#graph").graphviz()
    .width(300)
    .height(300)
    .fit(true)
    .scale(.5)
    .attributer(attributer)
    .renderDot('digraph  {a -> b}');
<script src="//d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/@hpcc-js/[email protected]/dist/index.min.js"></script>
<script src="https://unpkg.com/[email protected]/build/d3-graphviz.js"></script>
  
<div id="graph" style="width: 300px; height: 300px; border: 1px solid black"></div>
like image 185
magjac Avatar answered Oct 19 '22 17:10

magjac


Based on magjac's comment, I skipped .fit(), .scale(), .width(), and .height() and did it all in the attributer. This allows the solution to work even for larger graphs.

A few things to note:

  • Setting the height and width of the <svg> to 100% allows us to skip .width() and .height() and have the <svg> fill its container div.
  • Introduced a scale variable that can be set (0-1) to determine the scale of the graph
  • Added comments to help with anyone who finds their way here

Thank you magjac for this awesome library!

const scale = 0.8;

function attributer(datum, index, nodes) {
    var selection = d3.select(this);
    if (datum.tag == "svg") {
        datum.attributes = {
            ...datum.attributes,
            width: '100%',
            height: '100%',
        };
        // svg is constructed by hpcc-js/wasm, which uses pt instead of px, so need to convert
        const px2pt = 3 / 4;

        // get graph dimensions in px. These can be grabbed from the viewBox of the svg
        // that hpcc-js/wasm generates
        const graphWidth = datum.attributes.viewBox.split(' ')[2] / px2pt;
        const graphHeight = datum.attributes.viewBox.split(' ')[3] / px2pt;

        // new viewBox width and height
        const w = graphWidth / scale;
        const h = graphHeight / scale;

        // new viewBox origin to keep the graph centered
        const x = -(w - graphWidth) / 2;
        const y = -(h - graphHeight) / 2;

        const viewBox = `${x * px2pt} ${y * px2pt} ${w * px2pt} ${h * px2pt}`;
        selection.attr('viewBox', viewBox);
        datum.attributes.viewBox = viewBox;
    }
}

d3.select("#graph").graphviz()
    .attributer(attributer)
    .renderDot('digraph  {a -> b -> c ->d -> e}');
<script src="//d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/@hpcc-js/[email protected]/dist/index.min.js"></script>
<script src="https://unpkg.com/[email protected]/build/d3-graphviz.js"></script>
  
<div id="graph" style="width: 300px; height: 300px; border: 1px solid black"></div>
like image 2
tgordon18 Avatar answered Oct 19 '22 15:10

tgordon18