Drawing Chemical Structures with Force Layout

I’m trying to learn how to use the different layouts in D3, so I can start making more sophisticated visualizations. The lets you specify an array of nodes and links between those nodes, and the nature of the physical interaction between the nodes. I realized a few hours into playing around with it, that this could be a great toolbox for visualizing chemical structures. I’ll show you how in this post.

Let’s draw ! Alanine is the simplest amino acid, consisting of 3 carbons, 1 nitrogen, 2 oxygens, and 7 hydrogen atoms. Try clicking on an atom and dragging it!

Result

Method

To start, we need to define an array of nodes:

                     var nodes = [
                                {"atom":"C", "size":12},
                                {"atom":"C", "size":12},
                                {"atom":"C", "size":12},
                                {"atom":"N", "size":7},
                                {"atom":"H", "size":1},
                                {"atom":"O", "size":16},
                                {"atom":"O", "size":16},
                                {"atom":"H", "size":1},
                                {"atom":"H", "size":1},
                                {"atom":"H", "size":1},
                                {"atom":"H", "size":1},
                                {"atom":"H", "size":1},
                                {"atom":"H", "size":1} 
                        ],

Each object in the array represents one of the atoms. We’ll use the “atom” key later to draw the atomic symbol, and we’ll use the “size” key to vary the size of the node, which will be represented by a circle. An array of links is needed to connect the atoms:



                        links = [
                                {"source":0, "target":1},
                                {"source":1, "target":2},
                                {"source":1, "target":3},
                                {"source":2, "target":5},
                                {"source":2, "target":6},
                                {"source":1, "target":4},
                                {"source":3, "target":10},
                                {"source":3, "target":11},
                                {"source":0, "target":7},
                                {"source":0, "target":8},
                                {"source":0, "target":9},
                                {"source":5, "target":12}
                        ],

Next we start up the force layout, bring in the nodes and links, and set a few of the “physical” parameters:


                        force = d3.layout.force()
                                .nodes(nodes)
                                .links(links)
                                .charge(-1500)
                                .friction(0.8)
                                .gravity(0.5)
                                .size([w,h])
                                .start(),

Initialize the visualization elements for the links (“bonds”):


                        link = vis.selectAll("line")
                                .data(links)
                                .enter()
                                        .append("line")
                                        .attr("class","link"),

And do the same for the nodes (“atoms”):


                        node = vis.selectAll(".node")
                                .data(nodes)
                                .enter()
                                        .append("g")
                                        .attr("class","node")
                                        .call(force.drag); //allows you to drag atoms
                        
                                node.append("circle")
                                        .attr("r", function(d) {
                                                return Math.pow(40*d.size,1/3);
                                                })
                                        .attr("fill",function(d) {
                                                return fill(d.size);
                                                })
                                        .attr("stroke","black")
                                        .attr("stroke-width",2);
                                        
                                node.append("text")
                                        .attr("dx",function(d) {
                                                return Math.pow(40*d.size,1/3)+1;
                                                })
                                        .attr("dy",".35em")
                                        .text(function(d) {return d.atom;});

At each “tick” of the simulation, the following function updates the viz:


                        force.on("tick", function() {
                                link.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; });
                                        
                                node.attr("transform", function(d) { 
                                        return "translate(" + d.x + "," + d.y + ")"; 
                                        });             
                                });

This is a pretty good start for a chemical visualization library. I need to figure out how to implement double and triple bonds, and also figure out how to simulate different bond strengths.

Full Code

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

                        
<!DOCTYPE html>

                        
<html>

                        
<head>

                        
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script>

                        
<style type="text/css">

                        
.link {

                        
fill: steelblue;

                        
stroke: lightgray;

                        
stroke-width: 2px;

                        
}

                        
.node circle {

                        
stroke: solid black 1px;

                        
}

                        
 

                        
.node text {

                        
pointer-events: none;

                        
font: 14px sans-serif;

                        
color: blue;

                        
}

                        
</style>

                        
</head>

                        
<body>

                        
<script>

                        
var w=300,

                        
h=300,

                        
fill = d3.scale.category20(),

                        
nodes = [

                        
{"atom":"C", "size":12},

                        
{"atom":"C", "size":12},

                        
{"atom":"C", "size":12},

                        
{"atom":"N", "size":7},

                        
{"atom":"H", "size":1},

                        
{"atom":"O", "size":16},

                        
{"atom":"O", "size":16},

                        
{"atom":"H", "size":1},

                        
{"atom":"H", "size":1},

                        
{"atom":"H", "size":1},

                        
{"atom":"H", "size":1},

                        
{"atom":"H", "size":1},

                        
{"atom":"H", "size":1}

                        
],

                        

                        
links = [

                        
{"source":0, "target":1},

                        
{"source":1, "target":2},

                        
{"source":1, "target":3},

                        
{"source":2, "target":5},

                        
{"source":2, "target":6},

                        
{"source":1, "target":4},

                        
{"source":3, "target":10},

                        
{"source":3, "target":11},

                        
{"source":0, "target":7},

                        
{"source":0, "target":8},

                        
{"source":0, "target":9},

                        
{"source":5, "target":12}

                        
],

                        
 

                        
vis = d3.select("body").append("svg")

                        
.attr("width", w)

                        
.attr("height", h),

                        

                        
force = d3.layout.force()

                        
.nodes(nodes)

                        
.links(links)

                        
.charge(-1500)

                        
.friction(0.8)

                        
.gravity(0.5)

                        
.size([w,h])

                        
.start(),

                        

                        
link = vis.selectAll("line")

                        
.data(links)

                        
.enter()

                        
.append("line")

                        
.attr("class","link"),

                        

                        
node = vis.selectAll(".node")

                        
.data(nodes)

                        
.enter()

                        
.append("g")

                        
.attr("class","node")

                        
.call(force.drag);

                        

                        
node.append("circle")

                        
.attr("r", function(d) {

                        
return Math.pow(40*d.size,1/3);

                        
})

                        
.attr("fill",function(d) {

                        
return fill(d.size);

                        
})

                        
.attr("stroke","black")

                        
.attr("stroke-width",2);

                        

                        
node.append("text")

                        
.attr("dx",function(d) {

                        
return Math.pow(40*d.size,1/3)+1;

                        
})

                        
.attr("dy",".35em")

                        
.text(function(d) {return d.atom;});

                        

                        
force.on("tick", function() {

                        
link.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; });

                        

                        
node.attr("transform", function(d) {

                        
return "translate(" + d.x + "," + d.y + ")";

                        
});

                        
});

                        
</script>

                        
Chemical structure visualized using D3.

                        
</body>

                        
</html>

                      
hosted with ❤ by
Posted on by
Tagged :

Categories :layouts

Post comment as twitter logo facebook logo
Sort: Newest | Oldest