d3-graphviz arrows

I am creating graphs with d3-graphviz. I’m not able to fix the placement of the arrow tips on the edges. It seems that my the markers are not being picked up in my code. Here’s the URL:
https://observablehq.com/d/73573401fae74e7d

I am looking simply to move the arrows to the end of the edges between nodes.

1 Like

As a follow-on, I am trying to incorporate graphs like this in Framework. Cutting and pasting the code does not seem to work. Is d3@7 already part of Framework or do I need to import it? And are there any strange quirks to cutting and pasting the code into Framework.

As an aside, this seems like more work than it should be.

The marker positions are influenced by the refx and refy attributes of the marker definition. You seem to know this, as you’ve got:

svg.append("defs").append("marker")
  .attr("id", "arrowhead")
  .attr("viewBox", "0 -5 10 10")
  .attr("refX", nodeRadius + 3)  // Adjusted to place arrow at node edge
  .attr("refY", 0)

Your reference to nodeRadius in the definition of refX seems to suggest that you’re expecting the marker to be shifted back by exactly the radius of your nodes. The refX and refY attributes, though, refer to the coordinate system of the marker, which is different from the coordinate system of the containing figure. They’ve each got their own viewBox.

I’ve been hung up on this myself in the past and typically just fiddle with the code. I found that changing

  .attr("refX", nodeRadius + 3) 

to

  .attr("refX", 10) 

worked quite well.

Whether d3@7 is lazily loaded or not depends on whether you’re working within a .md file defining a page or a .js file defining a component. In the former case, you shouldn’t have to import d3; in the latter you do.

Perhaps. But then, the SVG specification is relatively low level compared to, say, a charting library. You’re manipulating SVG directly and programmatically using D3 and Javascript so it can’t really be any simpler than SVG is.


One final point - you’re not using d3-graphviz, which is a much higher level library and should, indeed, be much simpler. Observable’s dot template is built on top of d3-graphviz so you could use it. Your graph is defined by the following links:

links = [
  { source: "1", target: "B" },
  { source: "2", target: "B" },
  { source: "1", target: "A" },
  { source: "2", target: "A" },
  { source: "A", target: "B" }
]

Once you’ve got that, drawing something like your graph is as simple as so:

dot`digraph {
  rankdir=LR
  ${links.map((o) => `${o.source} -> ${o.target}`).join("; ")}
}`

Of course, you don’t have the groovy animated effect but maybe this is sufficient for your needs.

Thanks, Mark. This is very helpful. You are right to suggest that d3-graphviz is a better option. We need the interactivity but should take better advantage of the library.