Skip to content

Instantly share code, notes, and snippets.

@trebor
Last active September 29, 2016 18:52
Show Gist options
  • Save trebor/c5ef29301512f8325fa058d9092742eb to your computer and use it in GitHub Desktop.
Save trebor/c5ef29301512f8325fa058d9092742eb to your computer and use it in GitHub Desktop.
Nautiloid
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Nautiloid</title>
<link type="text/css" rel="stylesheet" href="nautiloid.css"/>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="./nautiloid.js"></script>
</head>
<body>
<div id="chart"></div>
</body>
</html>
html {
background: black;
}
* {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
padding: auto;
position: relative;
width: 100%;
height: 100%;
}
#chart {
}
svg {
-webkit-filter: drop-shadow(0 0 10px #ff0000);
}
.body {
fill: #888;
opacity: 0.25;
}
.node {
opacity: 0.25;
}
.node text{
fill: blue;
}
path.link {
fill: #848;
stroke: none;
opacity: 0.5;
}
.tentical {
fill: #FCF;
}
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,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment