Skip to content

Instantly share code, notes, and snippets.

@sughodke
Forked from mbostock/README.md
Last active January 5, 2018 22:26
Show Gist options
  • Save sughodke/1e72834e8272ae47f494d5b0bd5197d5 to your computer and use it in GitHub Desktop.
Save sughodke/1e72834e8272ae47f494d5b0bd5197d5 to your computer and use it in GitHub Desktop.
Ulam Spiral Quadtree 1

Ulam Archamedial Spiral

Ulam Spiral is a way of visualising prime numbers. We start with 1 in center and continue writing numbers in a rectangular spiral. Above, numbers in yellow are prime numbers. The spiral can be thought to demonstrate prime numbers form patterns.

Ulam Spiral Wikipedia page

Numberphile Youtube video

Adapted from Mike's Quadtree demo

<!DOCTYPE html>
<style>
.point {
fill: #000;
fill-opacity: 0.4;
}
.point--scanned {
fill: orange;
fill-opacity: 1;
stroke: orange;
stroke-width: 3px;
}
.point--selected {
fill: red;
fill-opacity: 1;
stroke: red;
stroke-width: 5px;
}
.node {
fill: none;
stroke: #ccc;
shape-rendering: crispEdges;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
zoom = 4,
selected;
var data = d3.range(10000)
.filter(isPrime)
.map(spiralCoords)
.map(function(coord) {
return [zoom*coord[0] + width/2, zoom*coord[1] + height/2];
});
var quadtree = d3.quadtree()
.extent([[-1, -1], [width + 1, height + 1]])
.addAll(data);
var brush = d3.brush()
.on("brush", brushed);
svg.selectAll(".node")
.data(nodes(quadtree))
.enter().append("rect")
.attr("class", "node")
.attr("x", function(d) { return d.x0; })
.attr("y", function(d) { return d.y0; })
.attr("width", function(d) { return d.y1 - d.y0; })
.attr("height", function(d) { return d.x1 - d.x0; });
var point = svg.selectAll(".point")
.data(data)
.enter().append("circle")
.attr("class", "point")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", 2);
svg.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, [[100, 100], [200, 200]]);
function brushed() {
var extent = d3.event.selection;
point.each(function(d) { d.scanned = d.selected = false; });
search(quadtree, extent[0][0], extent[0][1], extent[1][0], extent[1][1]);
point.classed("point--scanned", function(d) { return d.scanned; });
point.classed("point--selected", function(d) { return d.selected; });
}
// Find the nodes within the specified rectangle.
function search(quadtree, x0, y0, x3, y3) {
quadtree.visit(function(node, x1, y1, x2, y2) {
if (!node.length) {
do {
var d = node.data;
d.scanned = true;
d.selected = (d[0] >= x0) && (d[0] < x3) && (d[1] >= y0) && (d[1] < y3);
} while (node = node.next);
}
return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0;
});
}
// Collapse the quadtree into an array of rectangles.
function nodes(quadtree) {
var nodes = [];
quadtree.visit(function(node, x0, y0, x1, y1) {
node.x0 = x0, node.y0 = y0;
node.x1 = x1, node.y1 = y1;
nodes.push(node);
});
return nodes;
}
function isPrime(num) {
for(var i = 2; i < num; i++)
if(num % i === 0) return false;
return num !== 1;
}
// https://stackoverflow.com/a/20591835/343520
function spiralCoords(tileNum) {
var intRoot = Math.floor(Math.sqrt(tileNum));
var x = (Math.round(intRoot/2)*Math.pow(-1,intRoot+1)) + (Math.pow(-1,intRoot+1)*(((intRoot*(intRoot+1))-tileNum)-Math.abs((intRoot*(intRoot+1))-tileNum))/2);
var y = (Math.round(intRoot/2)*Math.pow(-1,intRoot))+(Math.pow(-1,intRoot+1)*(((intRoot*(intRoot+1))-tileNum)+Math.abs((intRoot*(intRoot+1))-tileNum))/2);
return [x, y];
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment