// Set-up the export button function set_export_button(svg, button_id, output_id) { d3.select('#'+button_id).on('click', function(){ //get svg source. var serializer = new XMLSerializer(); var source = serializer.serializeToString(svg.node()); console.log(source); source = source.replace(/^$/, ''); //add name spaces. if(!source.match(/^]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)){ source = source.replace(/^]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)){ source = source.replace(/^ colors[color]) node_enter.append('text') .attr("dx", -20) .attr("dy", -22) .style('fill', 'white') .text(({id, color, data}) => data.name); // Let's list the force we wanna apply on the network var simulation = d3.forceSimulation(data.nodes) // Force algorithm is applied to data.nodes .force("link", d3.forceLink().strength(0.1) // This force provides links between nodes .id(function(d) { return d.id; }) // This provide the id of a node .links(data.links) // and this the list of links ) .force("charge", d3.forceManyBody().strength(-400)) // This adds repulsion between nodes. Play with the -400 for the repulsion strength //.force("center", d3.forceCenter(_bbox.width / 2, _bbox.height / 2).strength(0.1)) // This force attracts nodes to the center of the svg area .on("tick", ticked); // This function is run at each iteration of the force algorithm, updating the nodes position. let scale_transform = {k: 1, x: 0, y: 0} function ticked() { 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_enter.attr("transform", function (d) { return 'translate('+scale_transform.x+','+scale_transform.y+') scale('+scale_transform.k+') translate('+d.x+','+d.y+')' }) } const drag = d3.drag() .on("start", dragstart) .on("drag", dragged); node_enter.call(drag).on('click', click); function click(event, d) { if (event.ctrlKey) { // Color cycle. d.color = (d.color + 1) % n_colors; d3.select(this).selectAll('circle').style("fill", ({id, color, data}) => colors[color]) } else { delete d.fx; delete d.fy; d3.select(this).classed("fixed", false); simulation.alpha(0.5).restart(); } } function dragstart() { d3.select(this).classed("fixed", true); } function dragged(event, d) { d.fx = event.x; d.fy = event.y; simulation.alpha(0.5).restart(); } const zoom = d3.zoom() .scaleExtent([0.01, 10]) .translateExtent([[-10000, -10000], [10000, 10000]]) .filter(filter) .on("zoom", zoomed); view.call(zoom); function zoomed({ transform }) { link.attr('transform', transform); scale_transform = transform; node_enter.attr("transform", function (d) { return 'translate('+scale_transform.x+','+scale_transform.y+') scale('+scale_transform.k+') translate('+d.x+','+d.y+')' }) redraw_func(); } // prevent scrolling then apply the default filter function filter(event) { event.preventDefault(); return (!event.ctrlKey || event.type === 'wheel') && !event.button; } } set_export_button(svg, 'saveButton', 'saveLink'); (async function() { // JANKY while (edit_id_output === undefined) { await sleep(500); } function redraw() { _bbox = bbox(); graph.attr("viewBox", [0, 0, _bbox.width, _bbox.height]); view.attr("width", _bbox.width - 1) .attr("height", _bbox.height - 1); } d3.select(window) .on("resize", function() { redraw(); }); redraw(); const data = convert_data(Array.from(all_nodes)); create_svg(data, redraw); console.log("render"); })();