// replace div data with translated (html output askama) // like this ? title: "{{"title1"|translate(lang)}}" let graph = { nodes : [ {id : "hemp", url: "/hemp" ,index: 0, x: 719.1790913580512, y: 326.11409230900296, vy: 0.0009665554475078223, vx: 0.00044710537175425926, title: "Title1", content: "Content 1"}, {id : "permapp", url: "/spider/permapp",index: 1, x: 473.51321690437135, y: 317.1181430586339, vy: 0.0005811239352358496, vx: 0.0005105059544352408, title: "Title 2", content: "Content 2"}, {id : "offgrid",url: "/offgrid", index: 2, x: 495.66982580572676, y: 18.53, vy: 0.0007713356454593206, vx: 0.0007887999665437037, title: "Title 3", content: "Content 3"}, {id : "cyberpreneur",url: "/cyberpreneur", index: 3, x: 457.9995206323447, y: 277.13445200140694, vy: 0.0004637678594351416, vx: 0.0007441570379881761, title: "Title 4", content: "Content 4" }, {id : "kaoscube",url: "/kaoscube", index: 4, x: 428.05791236956037, y: 237.65121337678465, vy: 0.00037483669542696317, vx: 0.00039028860355878376, title: "Title 5", content: "Content 5"}, {id : "spider", url: "/spider",index: 5, x: 461.65464520161396, y: 189.18158017082138, vy: -0.00023265081878199873, vx: 0.00006278089396472572, title: "Title 6", content: "Content 6" } ], links : [ {source: 0, target: 1, index: 0 }, {source: 0, target: 2, index: 1 }, {source: 0, target: 3, index: 2 }, {source: 0, target: 4, index: 3 }, {source: 1, target: 2, index: 4 }, {source: 1, target: 4, index: 5 }, {source: 2, target: 3 ,index: 6 }, {source: 2, target: 4 ,index: 7 }, {source: 2, target: 5 ,index: 8 }, {source: 4, target: 5 ,index: 9 } ], } // has to be made responsive to actual screensize ! let width = 100; let height = 200; let radius = 15; const svg = d3.select("#mobile_network").append("svg").attr("viewBox", "0, 0, 100, 200").attr("preserveAspectRatio", "xMidYMid meet").attr("style", "position: relative;"), link = svg .selectAll(".link") .data(graph.links) .join("line") .classed("link", true), node = svg .selectAll(".node") .data(graph.nodes) .join("circle") .attr("id", d => d.id) .attr("r", 15) .classed("node", true) .classed("fixed", d => d.fx !== undefined); const node1 = svg.select("#hemp") .attr("fill", "url(#imgHemp)") const node2 = svg.select("#permapp") .attr("fill", "url(#imgPermapp)"); const node3 = svg.select("#spider") .attr("fill", "url(#imgSpider)"); const node4 = svg.select("#cyberpreneur") .attr("fill", "url(#imgCyber)"); const node5 = svg.select("#kaoscube") .attr("fill", "url(#imgKaos)"); const node6 = svg.select("#offgrid") .attr("fill", "url(#imgOff)"); const defs = svg.append("svg:defs"); defs.append("svg:pattern") .attr("width", "100%") .attr("height", "100%") .attr("id", "imgHemp") .append("svg:image") .attr("xlink:href", "/assets/img/hemp.webp") .attr("width", 45) .attr("height", 45) .attr("x", -7 ) .attr("y", 0); defs.append("svg:pattern") .attr("width", "100%") .attr("height", "100%") .attr("id", "imgPermapp") .append("svg:image") .attr("xlink:href", "/assets/img/v1-permapp.webp") .attr("width", 35) .attr("height", 35) .attr("x", 0 ) .attr("y", 0); defs.append("svg:pattern") .attr("width", "100%") .attr("height", "100%") .attr("id", "imgSpider") .append("svg:image") .attr("xlink:href", "/assets/img/spider_out.webp") .attr("width", 45) .attr("height", 45) .attr("x", -4 ) .attr("y", -7); defs.append("svg:pattern") .attr("width", "100%") .attr("height", "100%") .attr("id", "imgCyber") .append("svg:image") .attr("xlink:href", "/assets/img/3d_greenhouse.webp") .attr("width", 50) .attr("height", 50) .attr("x", -5 ) .attr("y", -10); defs.append("svg:pattern") .attr("width", "100%") .attr("height", "100%") .attr("id", "imgKaos") .append("svg:image") .attr("xlink:href", "/assets/img/cube_space.webp") .attr("width", 40) .attr("height", 40) .attr("x", -6 ) .attr("y", -6); defs.append("svg:pattern") .attr("width", "100%") .attr("height", "100%") .attr("id", "imgOff") .append("svg:image") .attr("xlink:href", "/assets/img/boat_construction.webp") .attr("width", 40) .attr("height", 40) .attr("x", -5 ) .attr("y", -6); const simulation = d3 .forceSimulation() .nodes(graph.nodes) .force("charge", d3.forceManyBody().strength(-300)) .force("center", d3.forceCenter(width / 2.5, height / 2.25)) .force("link", d3.forceLink(graph.links).distance(30)) .force("collide", d3.forceCollide(22)) .force("x", d3.forceX(-30)) .on("tick", tick); const drag = d3 .drag() .on("start", dragstart) .on("drag", dragged); node.call(drag).on("click", click); // on doubleclick release position node.on("dblclick", release ); function release(event, d) { // back to original position delete d.fx; delete d.fy; simulation.alpha(1).restart(); } // make this work on touch as well ! function click(event, d) { d3.select(this).classed("fixed", false); } function dragstart() { // select element and set position to fixed d3.select(this).classed("fixed", true); // show box on single click toggleDiv(this.id, status); console.log("box atrr", box.attr("class")); if ( box.attr("class") == "box_on" ) { // make either nodes underneath unclickable as long as box is open or bring content in box to very front ! this.__on = null; } // this refers to dragged circle element console.log("dragstart", this.id); } function dragged(event, d) { console.log("dragged",d.id); // set x position of dragged node d.fx = clamp(event.x, 0, width); // set x position of dragged node d.fy = clamp(event.y, 0, height); // sets duration of animation before restart simulation.alpha(1).restart(); } function tick() { link .attr("x1", d => d.source.x) .attr("y1", d => d.source.y) .attr("x2", d => d.target.x) .attr("y2", d => d.target.y); node .attr("cx", d => d.x = Math.max(radius, Math.min(width - radius, d.x))) .attr("cy", d => d.y = Math.max(radius, Math.min(height - radius, d.y))); } function clamp(x, lo, hi) { return x < lo ? lo : x > hi ? hi : x; } // show info divs with data based on id // the infobox const box = d3.select("#box") // state of box let toggleDiv = undefined; // all nodes let nodeArray = graph.nodes; // change status of a box from visible to hidden and viceversa on click toggleDiv = function( event, status ) { if ( status == "" ) { status = box.attr("class") == "box_off" ? "box_on" : "box_off"; box.attr("class", status); } else { console.log("status is", status); } } // Like that somehow: embed data as html // Get content data //Parameters: data, array containing all nodes function getContent( n, nodeArray ) { info = '
'; if( n.link ) // get link of node info += ''; else console.log("couldn't get link:", n.url); if( n.title ) info += '

"' + n.title + '"/>' else console.log("couldn't get title:", n.title); if( n.content ) info += '

' console.log("couldn't get title:", n.content); return info; } // Show box for a given node function showContent( node ) { // Fill it and display the box box .html( getContent(node,nodeArray) ) }