A Hasse diagram drawn using the new D3v4 force-layout by specifying a d3.forceY constraint based on the distance from an arbitrary root node, in this case "FALSE".
| source | target | |
|---|---|---|
| FALSE | p&q | |
| FALSE | p&¬q | |
| FALSE | ¬p&q | |
| FALSE | ¬p&¬q | |
| p&q | p | |
| p&q | q | |
| p&q | p IFF q | |
| p&¬q | p | |
| p&¬q | p IFF ¬q | |
| p&¬q | (¬q) | |
| ¬p&q | p IFF ¬q | |
| ¬p&q | q | |
| ¬p&q | (¬p) | |
| ¬p&¬q | p IFF q | |
| ¬p&¬q | (¬q) | |
| ¬p&¬q | (¬p) | |
| p | pvq | |
| p | q IMPLIES p | |
| q | pvq | |
| q | p IMPLIES q | |
| p IFF q | q IMPLIES p | |
| p IFF q | p IMPLIES q | |
| p IFF ¬q | pvq | |
| p IFF ¬q | ¬pv¬q | |
| (¬q) | q IMPLIES p | |
| (¬q) | ¬pv¬q | |
| (¬p) | p IMPLIES q | |
| (¬p) | ¬pv¬q | |
| pvq | TRUE | |
| q IMPLIES p | TRUE | |
| p IMPLIES q | TRUE | |
| ¬pv¬q | TRUE |
| <html> | |
| <head> | |
| <title>d3v4 Boole Hasse Diagram</title> | |
| <meta charset="utf-8" /> | |
| <script src="https://d3js.org/d3.v4.0.0-alpha.33.min.js"></script> | |
| </head> | |
| <style> | |
| svg { | |
| height: 500px; | |
| width: 500px; | |
| border: 1px solid gray; | |
| } | |
| </style> | |
| <body> | |
| <div id="viz"> | |
| <svg class="main"> | |
| </svg> | |
| </div> | |
| </body> | |
| <footer> | |
| <script> | |
| d3.csv("hasse_boole.csv",function(error,data) {createNetwork(data)}); | |
| function onlyUnique(value, index, self) { | |
| return self.indexOf(value) === index; | |
| } | |
| function createNetwork(data) { | |
| var edges = data; | |
| var nodes = []; | |
| var nodeHash = {}; | |
| edges.forEach(function (d) { | |
| if (!nodeHash[d.source]) { | |
| var newNode = {id: d.source} | |
| nodes.push(newNode) | |
| nodeHash[d.source] = newNode; | |
| } | |
| if (!nodeHash[d.target]) { | |
| var newNode = {id: d.target} | |
| nodes.push(newNode) | |
| nodeHash[d.target] = newNode; | |
| } | |
| d.source = nodeHash[d.source]; | |
| d.target = nodeHash[d.target]; | |
| }) | |
| var rootNode = nodes.filter(function (d) {return d.id === "FALSE"})[0] | |
| rootNode.row = 0; | |
| friendlyBFS(rootNode); | |
| //Easy to do because this is a simple DAG | |
| function friendlyBFS(node) { | |
| var targets = edges.filter(function (d) {return d.source === node}).map(function (d) {return d.target}); | |
| targets.forEach(function (d) { | |
| d.row = node.row + 1; | |
| friendlyBFS(d) | |
| }) | |
| } | |
| var networkCenter = d3.forceCenter().x(250).y(250); | |
| var manyBody = d3.forceManyBody().strength(-1000) | |
| var linkForce = d3.forceLink(edges).id(function (d) {return d.id}).distance(30).iterations(1) | |
| //Generic gravity for the X position | |
| var forceX = d3.forceX(function (d) {return 250}) | |
| .strength(0.05) | |
| //strong y positioning based on row | |
| var forceY = d3.forceY(function (d) {return d.row * 50}) | |
| .strength(0.5) | |
| var force = d3.forceSimulation(nodes) | |
| .force("charge", manyBody) | |
| .force("link", linkForce) | |
| .force("center", networkCenter) | |
| .force("x", forceX) | |
| .force("y", forceY) | |
| .force("collide", function (d) {return 50}) | |
| .on("tick", updateNetwork); | |
| var edgeEnter = d3.select("svg.main").selectAll("g.edge") | |
| .data(edges) | |
| .enter() | |
| .append("g") | |
| .attr("class", "edge"); | |
| edgeEnter | |
| .append("line") | |
| .style("stroke-width", function (d) {return d.border ? "3px" : "1px"}) | |
| .style("stroke", "black") | |
| .style("pointer-events", "none"); | |
| var nodeEnter = d3.select("svg.main").selectAll("g.node") | |
| .data(nodes, function (d) {return d.id}) | |
| .enter() | |
| .append("g") | |
| .attr("class", "node") | |
| nodeEnter.append("ellipse") | |
| .attr("rx", 28) | |
| .attr("ry", 14) | |
| .style("fill", "white") | |
| .style("stroke", "black") | |
| .style("stroke-width", function (d) {return d.border ? "3px" : "1px"}) | |
| nodeEnter.append("text") | |
| .style("text-anchor", "middle") | |
| .attr("y", 3) | |
| .style("stroke-width", "1px") | |
| .style("stroke-opacity", 0.75) | |
| .style("stroke", "white") | |
| .style("font-size", "8px") | |
| .text(function (d) {return d.id}) | |
| .style("pointer-events", "none") | |
| nodeEnter.append("text") | |
| .style("text-anchor", "middle") | |
| .attr("y", 3) | |
| .style("font-size", "8px") | |
| .text(function (d) {return d.id}) | |
| .style("pointer-events", "none") | |
| function updateNetwork(e) { | |
| d3.select("svg.main").selectAll("line") | |
| .attr("x1", function (d) {return d.source.x}) | |
| .attr("y1", function (d) {return d.source.y}) | |
| .attr("x2", function (d) {return d.target.x}) | |
| .attr("y2", function (d) {return d.target.y}); | |
| d3.select("svg.main").selectAll("g.node") | |
| .attr("transform", function (d) {return "translate(" + d.x + "," + d.y + ")"}); | |
| } | |
| } | |
| </script> | |
| </footer> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment