var margin = {top: 10, right: 10, bottom: 10, left: 10}; var padding = {top: 0, right: 0, bottom: 0, left: 0}; var outerWidth; var outerHeight; var innerWidth; var innerHeight; var width; var height; var svg; var force; var nextName = 0; var depth = 5; var armLengthScale = d3.scale.linear().range([3, 3]); var degreeScale = d3.scale.linear().range([1, 3]); var radiusScale = d3.scale.pow().exponent(.8).range([25, 0]); var distanceScale = d3.scale.pow().exponent(1).range([20, 10]); var linkStrengthScale = d3.scale.linear().range([10, 10]); var timeFactorScale = d3.scale.linear().range([1/300, 1/5000]); var waggleFactorScale = d3.scale.linear().range([.005, .007]); var chargeScale = d3.scale.linear().range([-1, -1]); var root; var nodes = []; var links = []; //var joints = []; var mouse; var currentSegmentId = 0; function getNextSegmentId() { return currentSegmentId++; } $(document).ready(function() { console.log("ready!"); init(); }); function init() { // size stuff outerWidth = $("#chart").width(); outerHeight = $("#chart").height(); innerWidth = outerWidth - margin.left - margin.right; innerHeight = outerHeight - margin.top - margin.bottom; width = innerWidth - padding.left - padding.right; height = innerHeight - padding.top - padding.bottom; // init svg svg = d3.select("#chart") .append("svg") .attr("width", outerWidth) .attr("height", outerHeight) .on("mousemove", function() {mouse = d3.event}) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // create fdl force = d3.layout.force() .gravity(0.0) .friction(.9) .distance(function(link) {return distanceScale(link.target.depth)}) .charge(function(node) {return chargeScale(node.depth)}) .linkStrength(function(link) {return linkStrengthScale(link.source.depth)}) .size([width, height]); // create root ndoe root = { name: "root", x: width / 2, y: height / 2, depth: 0, fixed: true, children: [], }; // construct tree var tree = generateTree( root, depth, function() {return degreeScale(Math.random())}, function() {return armLengthScale(Math.random())}, nodes, links); // joints = links.filter(function(link) {return true; link.joint;}); // joints.sort(function(j1, j2) {return j1.source.depth - j2.source.depth}); // joints.forEach(function(joint) { // joint.timeFactor = timeFactorScale(Math.random()); // joint.waggleFactor = waggleFactorScale(Math.random()); // }); // console.log(d3.extent(joints, function(j) {return j.timeFactor;}).map(function(d) {return Math.round(d * 100000)})); nodes.push(root); // configure scales distanceScale.domain(d3.extent(nodes, function(d) {return d.depth})); radiusScale.domain(d3.extent(nodes, function(d) {return d.depth})); linkStrengthScale.domain(d3.extent(nodes, function(d) {return d.depth})); chargeScale.domain(d3.extent(nodes, function(d) {return d.depth})); // precompute node and link properties nodes.forEach(function(node) { node.radius = radiusScale(node.depth); }); links.forEach(function(link) { link.distance = link.source.radius + link.target.radius * 2; }) update(); } function generateTree(root, maxDepth, degreeFunc, lengthFunc, nodes, links, depth) { depth = depth || 0; nodes = nodes || []; links = links || []; var degree = degreeFunc(); var length = lengthFunc(); var segmentId = getNextSegmentId(); // for each degree for (var d = 0; d < degree; ++d) { // the source node of each link var source = root; // for length for (var l = 0; l < length; ++l) { var effectiveDepth = depth * length + l + 1; var node = { name: String.fromCharCode(65 + nextName++), segmentId: segmentId, depth: effectiveDepth, x: width / 2, y: height / 2, group: 1, children: [], }; nodes.push(node); links.push({ joint: source == root, source: source, target: node, value: 1, }); source.children.push(node); source = node; } if (depth < maxDepth - 1) generateTree(source, maxDepth, degreeFunc, lengthFunc, nodes, links, depth + 1); } return {nodes: nodes, links: links}; } function treePoints(root, points) { points = points || []; var d90 = Math.PI / 2; root.children.forEach(function(child) { var angle = child.angle; points.push(polarPoint(root, root.radius, angle + d90)); points.push(polarPoint(child, child.radius, angle + d90)); treePoints(child, points); points.push(polarPoint(child, child.radius, angle - d90)); points.push(polarPoint(root, root.radius, angle - d90)); }); return points; } function sortChildren(root) { root.children.forEach(function(child) { child.angle = polarAngle(root, child); sortChildren(child); }); root.children.sort(function(c1, c2) { return c2.angle - c1.angle; }); } function bodyPath() { sortChildren(root); var tp1 = treePoints(root); var tp2 = []; for (var i = 1; i < tp1.length; i += 2) { var i1 = i; var i2 = (i + 1) % tp1.length; // console.log(i1, i2, tp1.length); tp2.push(avergePoints(tp1[i1], tp1[i2])); } // debugger; return populatePoloy(tp1); } function linkPath(link) { var s = link.source; var t = link.target; var angle = polarAngle(s, t); var d90 = Math.PI / 2; var points = [ polarPoint(s, s.radius, angle + d90), polarPoint(s, s.radius, angle - d90), polarPoint(t, t.radius, angle - d90), polarPoint(t, t.radius, angle + d90), ]; return populatePoloy(points); } var colors = d3.scale.category20(); function update() { force .nodes(nodes) .links(links) .start(); // var tentical = svg // .append("circle") // .attr("class", "tentical") // .attr("r", 50); // var link = svg.selectAll(".link") // .data(links) // .enter() // .append("path") // // .style("stroke-width", function(d) {return d.target.radius * 2}) // .attr("linecap", "round") // .attr("class", "link"); var body = svg .append("path") .attr("class", "body"); var node = svg.selectAll(".node") .data(nodes) .enter().append("g") .attr("class", "node") .call(force.drag); node.append("circle") .attr("cx", 0) .attr("cy", 0) .style("fill", function(d) {return colors(d.segmentId)}) .attr("r", function(d) {return d.radius}); // node.append("text") // .attr("dx", 12) // .attr("dy", ".35em") // .text(function(d) { return d.name }); var lastTime = new Date().getTime(); force.on("tick", function() { if (mouse) { var bias = 0.99; root.px = root.x * bias + mouse.x * (1 - bias); root.py = root.y * bias + mouse.y * (1 - bias); } var now = new Date().getTime(); dTime = now - lastTime; lastTime = now; // joints.forEach(function(joint) { // var s = joint.source; // var t = joint.target; // var theta = polarAngle(s, t); // var radius = distance(s, t); // var dTheta = Math.sin(now * joint.timeFactor) * joint.waggleFactor; // // console.log(Math.round(dTheta * 10000)); // theta += dTheta; // polarPoint(s, radius, theta, t); // }); //link.attr("d", linkPath); body.attr("d", bodyPath); // 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; }); // var nodesX = 0; // var nodesY = 0; // nodes.forEach(function(node) { // nodesX += node.x; // nodesY += node.y; // }); // nodesX /= nodes.length; // nodesY /= nodes.length; // tentical // .attr("cx", nodesX) // .attr("cy", nodesY); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); force.resume(); }); } function polarAngle(p1, p2) { var dx = p2.x - p1.x; var dy = p2.y - p1.y; return Math.atan2(dy, dx); } function polarPoint(p1, radius, theta, p2) { p2 = p2 || {}; p2.x = p1.x + radius * Math.cos(theta); p2.y = p1.y + radius * Math.sin(theta); return p2; } function distance(p1, p2) { var dx = p2.x - p1.x; var dy = p2.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } function populatePath(path, points) { for(index in points) { path = path .replace("X" + index, points[index].x) .replace("Y" + index, points[index].y); }; return path; } function populatePoloy(points) { var path = ""; points.forEach(function(point, i) { path += (i == 0 ? "M" : "L") + point.x + " " + point.y + " "; }) path += "z"; return path; } function avergePoints(p1, p2) { return { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2, }; } function Segment(length, parent) { var segmentId = getNextSegmentId(); var depth = parent ? parent.getDepth() + 1 : 0; var children = []; var nodes = []; var links = []; function init() { // the source node of each link var source = parent ? parent.getJoint() : undefined; // for length for (var l = 0; l < length; ++l) { var effectiveDepth = depth * length + l + 1; var node = { name: String.fromCharCode(65 + nextName++), segmentId: segmentId, depth: effectiveDepth, x: width / 2, y: height / 2, group: 1, children: [], }; nodes.push(node); links.push({ joint: source == root, source: source, target: node, value: 1, }); source.children.push(node); source = node; } if (depth < maxDepth - 1) generateTree(source, maxDepth, degreeFunc, lengthFunc, nodes, links, depth + 1); } function getDepth() { return depth; } function getPath() { } function getNodes() { return nodes; } function getLinks() { return links; } function getJoint() { return nodes[length - 1]; } return { getPath: getPath, getNodes: getNodes, getLinks: getLinks, getJoint: getJoint, getDepth: getDepth, } }