Skip to content

Instantly share code, notes, and snippets.

@TVerduyn
Last active October 4, 2018 12:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TVerduyn/4935615ba55a2c0b73b2550e0eb8764d to your computer and use it in GitHub Desktop.
Save TVerduyn/4935615ba55a2c0b73b2550e0eb8764d to your computer and use it in GitHub Desktop.
Keypad Code Guessing Visualisation
<!DOCTYPE html>
<!--Code Lock Graph-->
<meta charset="utf-8">
<style>
body { font-family: Helvetica, Arial, sans-serif; }
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<html>
<Title>Keypad Code Guessing Visualisation</Title>
(Base, #Characters):
<select id="BaseCharCount" size="1">
<option value="1,1">(2,2)</option>
<option value="1,2">(2,3)</option>
<option value="1,3">(2,4)</option>
<option value="1,4">(2,5)</option>
<option value="1,5">(2,6)</option>
<option value="1,6">(2,7)</option>
<option value="1,7">(2,8)</option>
<option value="2,1">(3,2)</option>
<option value="2,2">(3,3)</option>
<option selected="selected" value="2,3">(3,4)</option>
<option value="2,4">(3,5)</option>
<option value="3,1">(4,2)</option>
<option value="3,2">(4,3)</option>
<option value="3,3">(4,4)</option>
<option value="4,1">(5,2)</option>
<option value="4,2">(5,3)</option>
<option value="5,1">(6,2)</option>
<option value="5,2">(6,3)</option>
<option value="6,1">(7,2)</option>
<option value="7,1">(8,2)</option>
<option value="8,1">(9,2)</option>
<option value="9,1">(10,2)</option>
</select>
Speed:
<input type="range" min="-1500" max="-100" value="-500" id="Timer" width=90>
<input type="button" onclick="draw(+BaseCharCount.value[0]+1,+BaseCharCount.value[2]+1,+-Timer.value);" value="Redraw">
<div id="vis"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("#vis")
.append("svg")
draw(3,4,500);
function draw(basecount, charcount, timer) {
var totalwidth = (window.innerWidth > 0) ? window.innerWidth : screen.width;
var totalheight = (window.innerHeight > 0) ? window.innerHeight : screen.height;
var width = totalwidth-50
var height = totalheight-50
svg.attr("width", width)
.attr("height", height);
d3.select("svg").selectAll("text").remove()
d3.select("svg").selectAll("g").remove()
nodes = [];
links = [];
currentNode = '';
currentLink = '';
for ( i = 0; i < Math.pow(basecount, charcount); i++) {
currentNode = i.toString(basecount).padStart(charcount, '0')
nodes.push({"id":currentNode})
for ( j = 0; j < basecount; j++) {
currentLink = currentNode.substring(1, charcount) + j.toString()
links.push({"source":currentNode,"target":currentLink});
currentLink = '';
}
}
var color = d3.scaleOrdinal(d3.schemeCategory20);
function x_limit_force() {
for (var i = 0, n = nodes.length; i < n; ++i) {
nodes[i].x = Math.max(10 - nodes[i].vx , Math.min(width - 10 - nodes[i].vx, nodes[i].x));
nodes[i].y = Math.max(25 - nodes[i].vy , Math.min(height - 5 - nodes[i].vy, nodes[i].y));
}
}
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).distance(75).strength(0))
.force("charge", d3.forceManyBody().strength(-1))
.force("collide", d3.forceCollide(20).radius(30).strength(0.5))
.force("center", d3.forceCenter(width / 2, height / 2))
.alpha(0.45)
;
svg.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("line")
.attr("stroke-width", 1)
.attr("opacity", 0.7)
.attr("marker-end", "url(#end)");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter()
.append("text")
.attr("font-family", "Helvetica, Arial, sans-serif")
.attr("font-size", 12)
.text(function(d) { return d.id})
.attr("x", function(d) { return d.x})
.attr("y", function(d) { return d.y})
.attr("fill", "#2c7bb6")
.attr("opacity",0.7)
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
var dBsequence = deBruijn (basecount, charcount).join("");
var textstart = svg.append("text")
.attr("id","other")
.attr("x", 10)
.attr("y", 15)
.attr("font-size", 12)
.text(function() { return "De Bruijn Sequence: " ; })
var textdBprestart = textstart.append("tspan")
.attr("fill","red")
.attr("font-weight","bold")
.text(function() { return dBsequence.substring(0, 0); })
var textdBstart = textstart.append("tspan")
.attr("fill","black")
.text(function() { return dBsequence.substring(0, 0); })
var textdBmid = textstart.append("tspan")
.attr("fill","red")
.attr("font-weight","bold")
.text(function() { return dBsequence.substring(0, charcount); })
var textdBend = textstart.append("tspan")
.attr("fill","black")
.text(function() { return dBsequence.substring(charcount); })
for ( i = 0; i < dBsequence.length; i++) {
svg.select(".nodes").selectAll('text')
.filter(function(d){ return d.id==[dBsequence,dBsequence].join("").substring(i, i+charcount)})
.transition()
.duration(timer)
.delay(2000+i*timer)
.attr("opacity",1)
.attr("fill", "red")
.transition()
.duration(timer)
.delay(timer)
.attr("opacity", 0.3)
svg.selectAll("line")
.filter(function(d){ return d.source.id==[dBsequence,dBsequence].join("").substring(i, i+charcount)})
.filter(function(d){ return d.target.id==[dBsequence,dBsequence].join("").substring(i+1, i+charcount+1)})
.transition()
.duration(timer)
.delay(2000+(i+1)*timer)
.attr("opacity", 1)
.style("stroke", "#d7191c")
.transition()
.duration(timer)
.delay(timer)
.attr("opacity", 0.3)
textdBstart
.transition()
.duration(timer)
.delay(2000+(i+0.5)*timer)
.text(function() { return dBsequence.substring(0, i); });
textdBmid
.transition()
.duration(timer)
.delay(2000+(i+0.5)*timer)
.text(function() { return dBsequence.substring(i, i+charcount); });
textdBend
.transition()
.duration(timer)
.delay(2000+(i+0.5)*timer)
.text(function() { return dBsequence.substring(i+charcount); });
if (i>dBsequence.length-charcount){
textdBprestart
.transition()
.duration(timer)
.delay(2000+(i+0.5)*timer)
.text(function() { return dBsequence.substring(0, i+charcount-dBsequence.length); });
textdBstart
.transition()
.duration(timer)
.delay(2000+(i+0.5)*timer)
.text(function() { return dBsequence.substring(i+charcount-dBsequence.length, i); });
}
}
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
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
for (var i = 0, n = nodes.length; i < n; ++i) {
if( nodes[i].x + nodes[i].vx <0 || nodes[i].x + nodes[i].vx > width-50 || nodes[i].y + nodes[i].vy < 30 || nodes[i].y + nodes[i].vy > height){
simulation.stop()
}
}
}
}
function deBruijn (k, n) {
var a = [];
for (var i = 0; i < k * n; i++) a.push(0);
var sequence = [];
(function db (t, p) {
if (t > n) {
if (n % p !== 0) return;
for (var j = 1; j <= p; j++) {
sequence.push(a[j]);
}
return;
}
a[t] = a[t-p];
db(t + 1, p);
for (var j = a[t-p] + 1; j < k; j++) {
a[t] = j;
db(t + 1, t);
}
})(1,1);
return sequence
};
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment