D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
chemplexity
Full window
Github gist
Dynamic Molecules Generator
<!DOCTYPE html> <meta charset="utf-8"> <style> * { box-sizing:border-box; } .link line { stroke: #000000; stroke-opacity: 0.7; stroke-width: 1; } .link line.separator { stroke: #fff; stroke-width: 3px; } .node circle { stroke: #000; stroke-width: 3px; } .container { font-family:'Lucida Sans Unicode'; width:500px; margin:15px auto auto 30px; display:block; background:#FFF; padding:20px 5px 5px; } .footer { text-align:right; } .footer a { color:#53B2C8; } .group { position:relative; margin-bottom:5px; } .examples { color:#999; font-size:12px; font-weight:normal; font-family:'Lucida Sans Unicode'; } .examples a:link { color: #53B2C8; font-weight:bold; } .examples a:visited { color: #53B2C8; font-weight:bold; } .examples a:hover { color: #53B2C8; font-weight:bold; } .examples a:active { color: #53B2C8; font-weight:bold; } input { font-size:16px; padding:10px 10px 10px 5px; display:block; width:300px; border:none; border-bottom:1px solid #757575; } input:focus { outline:none; } label { color:#999; font-size:16px; font-weight:normal; position:absolute; pointer-events:none; left:5px; top:10px; transition:0.2s ease all; -moz-transition:0.2s ease all; -webkit-transition:0.2s ease all; } input:focus ~ label, input:valid ~ label { top:-15px; font-size:13px; color:#5264AE; } .bar { position:relative; display:block; width:350px; } .bar:before, .bar:after { content:''; height:2px; width:0; bottom:1px; position:absolute; background:#5264AE; transition:0.2s ease all; -moz-transition:0.2s ease all; -webkit-transition:0.2s ease all; } .bar:before { left:50%; } .bar:after { right:50%; } input:focus ~ .bar:before, input:focus ~ .bar:after { width:50%; } .highlight { position:absolute; height:60%; width:100px; top:25%; left:0; pointer-events:none; opacity:0.5; } input:focus ~ .highlight { -webkit-animation:inputHighlighter 0.3s ease; -moz-animation:inputHighlighter 0.3s ease; animation:inputHighlighter 0.3s ease; } @-webkit-keyframes inputHighlighter { from { background:#5264AE; } to { width:0; background:transparent; } } @-moz-keyframes inputHighlighter { from { background:#5264AE; } to { width:0; background:transparent; } } @keyframes inputHighlighter { from { background:#5264AE; } to { width:0; background:transparent; } } </style> <script src="smiles.js"></script> <script src="d3.v3.min.js"></script> <body> <div class="container"> <form onsubmit="return false"> <div id="user" class="group"> <input type="text" required value="" onpaste="onclick_handler(this.value)" onkeyup="onkey_handler(event)"> <span class="highlight"></span> <span class="bar"></span> <label id="textbox">Input...</label> </div> </form> <label> <div id="molecularWeight" style="position: absolute; top:35px; left:400px; width:200px; height:25px">""</div> </label> <div id="examples" style="position: absolute; top:68px; left:46px; width:400px; height:30px"> <p class="examples"><i>examples: </i> <a href="#" onclick="example_handler('C=O C=O C=O C=O C=O C=O C=O C=O C=O')">formaldehyde</a>, <a href="#" onclick="example_handler('FH ClH BrH IH FH ClH BrH IH FH ClH BrH IH')">halides</a>, <a href="#" onclick="example_handler('C=C(C)C=C')">isoprene,</a> <a href="#" onclick="example_handler('NC(C=O)C=C(O)C(N)C')">default</a> </p> </div> <div id="chart"></div> <div><p class="footer"> <a href="https://github.com/chemplexity/molecules" target="_blank">source</a>, <a href="https://www.opensmiles.org/spec/open-smiles-2-grammar.html" target="_blank">help</a> </p></div> </div> </body> <script> /** * Generate dynamic molecules with HTML/CSS/Javascript * (C) 2014 by James Dillon * https://wwww.github.com/chemplexity */ // Filter input var onkey_handler = function(event) { // Input: 'enter', 'backspace', '=', '0', '9' if (event.keyCode == 13 || event.keyCode == 8 || event.keyCode == 187 || event.keyCode == 48 || event.keycode == 57) {onclick_handler(document.getElementsByTagName("input")[0].value)} // Input: non-alphabet else if (event.keyCode < 65 || event.keyCode > 90) {return false} // Input: alphabet else {onclick_handler(document.getElementsByTagName("input")[0].value)} } var example_handler = function (input) { document.getElementsByTagName("input")[0].value = input; onclick_handler(input); } // Submit input var onclick_handler = function(input) { data(input) }; // SMILES to JSON var data = function(input) { // Check for empty input if (input.length == 0) {return false} // Use custom SMILES converter var molecule = smiles(input); // Add size property to atoms for (i=0; i < molecule.atoms.length; i++) { if(molecule.atoms[i].element == 1) { molecule.atoms[i].size = 5 } else if(molecule.atoms[i].element == 9) { molecule.atoms[i].size = 6 } else if(molecule.atoms[i].element == 35) { molecule.atoms[i].size = 11 } else if(molecule.atoms[i].element == 53) { molecule.atoms[i].size = 13 } else { molecule.atoms[i].size = 8 } } // Add source/target fields for (i=0; i < molecule.bonds.length; i++) { molecule.bonds[i].source_id = molecule.bonds[i].source; molecule.bonds[i].target_id = molecule.bonds[i].target; } // Update graph update(molecule); }; // General properties var margin = {top: 5, right: -5, bottom: 5, left: -5}; var width = 500 - margin.left - margin.right, height = 375 - margin.top - margin.bottom, padding = 1; r = 15; // Size of atoms var radius = d3.scale.sqrt() .range([0, 6]); // Graphics container var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); // Molecules container var vis = d3.select("#chart") .append("svg:svg") .attr("width", width) .attr("height", height) .append("g") // Start d3.js var force = d3.layout.force() .size([width + margin.left + margin.right, height + margin.top + margin.bottom]) .charge(-300) .gravity(0.8) .linkDistance(-40) .theta(0.1) // Update graph with new molecule function update(molecule) { // Prevent bonds from overlapping var bonding = []; // Create invisible nodes between atoms molecule.bonds.forEach(function(link) { bonding.push({ source: molecule.atoms[link.source], target: molecule.atoms[link.target] }); }); // Concatenate invisible nodes with atoms force.nodes(molecule.atoms.concat(bonding)) force.links(molecule.bonds) vis.selectAll(".node").remove() // Initialize bonds var link = vis.selectAll(".link") .data(force.links(), function(d) { return d.source +"-"+ d.target; }) // Update bonds link.enter().append("g") .attr("class", "link") .on('mouseover', function (d) {return update_fragment(molecule, d.source, d.target)}) //.on('mouseout', bondType) link.append("line") .style("stroke-width", function(d) { return (d.order * 3 - 1) * 2 + "px"; }) link.filter(function(d) { return d.order > 1; }).append("line") .attr("class", "separator"); // Remove bonds from last molecule link.exit().remove() // Set rigid bonds var bonding = vis.selectAll(".link-node") .data(bonding) bonding.enter().append("circle") .attr("class", "link-node") .attr("visibility", "hidden"); bonding.exit().remove() // Initialize atoms var node = vis.selectAll(".node") .data(molecule.atoms, function(d) {return d.atom_id}) // Update atoms node.enter().append("circle") .attr("class", "node") .call(force.drag) .style("fill", function(d) { return d.color; }) .attr("r", function(d) { return d.size+2 }) .style("stroke", "black") .style("stroke-width", 3) .style("stroke-opacity", 0.9); // Remove atoms from last molecule node.exit() .attr('x', 0) .transition() .duration(100) .attr('x', 60) .style('fill-opacity', 0) .remove() // Dynamic movement force.on("tick", function () { link.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; }) node.attr("cx", function(d) { return d.x = Math.max(r, Math.min(width - r, d.x)); }) .attr("cy", function(d) { return d.y = Math.max(r, Math.min(height - r, d.y)); }) bonding.attr("cx", function(d) { return d.x = (d.source.x + d.target.x) * 0.5; }) .attr("cy", function(d) { return d.y = (d.source.y + d.target.y) * 0.5; }); }); force.start() // Update molecular weight values update_text(molecule) } // Display molecular weight function update_text(molecule) { document.getElementById("molecularWeight").innerHTML = molecule.molecule_weight + " g/mol"; } // Display fragmentation molecular weight function update_fragment(molecule, source, target) { // Selected bond data var bondSelected = d3.select(this).node() // Calculate molecular weight of fragments var sourceFragment = 0; var targetFragment = 0; var totalFragment = []; //console.log(findObjects(molecule.bonds, "source_id", source.index)); //var x = findObjects(molecule.bonds, "source_id"; //console.log(source.index) } // Default molecule data("NC(C=O)C=C(O)C(N)C"); // // Utility // // Find object key/value function findObjects(array, key, value) { for (var i = 0; i < array.length; i++) { if (array[i][key] == value) { return array[i]; }} return null } /** function bondType() { // Selected bond data var bondSelected = d3.select(this).node().__data__ // Calculate molecular weight of fragments var sourceFragment = 0; var targetFragment = 0; var totalFragment = []; // Traverse nodes for (i = 0; i < molecule.atoms.length; i++) { // Current target var targetAtom = bondSelected.target; if (findObjects(totalFragment, "id", targetAtom) == null) { totalFragment.push({id: bondSelected.target}) } // for (i = 0; i < molecule.nodes.length; i++) { linkIndex[i + "," + i] = 1; } // molecule.links.forEach(function(d) { linkIndex[d.source.index + "," + d.target.index] = 1; }); // function neighboring(a, b) { return linkIndex[a.index + "," + b.index]; } console.log(d3.select(this).node().__data__) } */ </script>