Fork of: http://bl.ocks.org/mbostock/5649592
To show: http://glowingpython.blogspot.com/2013/05/a-colorful-trifoil-knot.html
TODO: Figure out how to encode height along path as color or above and below at line crossings.
Fork of: http://bl.ocks.org/mbostock/5649592
To show: http://glowingpython.blogspot.com/2013/05/a-colorful-trifoil-knot.html
TODO: Figure out how to encode height along path as color or above and below at line crossings.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<body> | |
<style> | |
path { | |
fill: none; | |
stroke: #000; | |
stroke-width: 3px; | |
} | |
circle { | |
fill: steelblue; | |
stroke: #fff; | |
stroke-width: 3px; | |
} | |
</style> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script> | |
<script> | |
var range = function (start, end, step) { | |
var result = []; | |
for (var i = start; i < end; i += step) { | |
result.push(i); | |
} | |
return result; | |
} | |
var options = { | |
size: 500, // canvas width and height | |
interpolation: "basis-closed", | |
samples: 8, // points along parameterized function | |
showSamples: false | |
}; | |
var draw = function () { | |
var sin = Math.sin; | |
var cos = Math.cos; | |
var pi = Math.PI; | |
var size = options.size; | |
var samples = options.samples; | |
var phis = range(0, pi * 2, (pi * 2) / samples); | |
var points = phis.map(function (phi) { | |
return [sin(phi) + 2*sin(2*phi), cos(phi) - 2*cos(2*phi)]; | |
}); | |
points = points.map(function (point) { | |
return point | |
.map(function (coord) { return coord * (size / 8) }) // scale | |
.map(function (coord) { return coord + (size / 2) }); // center | |
}); | |
var line = d3.svg.line() | |
.interpolate(options.interpolation); | |
// out with the old | |
d3.select("body svg").remove(); | |
var svg = d3.select("body") | |
.append("svg") | |
.datum(points) | |
.attr("width", size) | |
.attr("height", size); | |
svg.append("path") | |
.style("stroke", "#ddd") | |
.attr("d", line); | |
svg.append("path") | |
.attr("d", line) | |
.call(transition); | |
if (options.showSamples) { | |
svg.selectAll('.point') | |
.data(points) | |
.enter() | |
.append("circle") | |
.attr("r", 4) | |
.attr("transform", function(d) { return "translate(" + d + ")"; }); | |
} | |
function transition(path) { | |
path.transition() | |
.duration(7500) | |
.attrTween("stroke-dasharray", tweenDash) | |
.each("end", function() { d3.select(this).call(transition); }); | |
} | |
function tweenDash() { | |
var l = this.getTotalLength(), | |
i = d3.interpolateString("0," + l, l + "," + l); | |
return function(t) { return i(t); }; | |
} | |
} | |
var gui = new dat.GUI(); | |
gui.add(options, 'size', 0, 2000) | |
.step(10) | |
.onChange(draw); | |
gui.add(options, 'samples', 0, 16) | |
.step(1) | |
.onChange(draw); | |
var interpolatorNames = [ | |
"linear", | |
"step-before", | |
"step-after", | |
"basis", | |
"basis-open", | |
"basis-closed", | |
"cardinal", | |
"cardinal-open", | |
"cardinal-closed", | |
"monotone" | |
] | |
gui.add(options, 'interpolation') | |
.options(interpolatorNames) | |
.onChange(draw); | |
gui.add(options, 'showSamples') | |
.onChange(draw); | |
draw(); | |
</script> |