Skip to content

Instantly share code, notes, and snippets.

@topologicallytony
Last active January 6, 2017 03:30
Show Gist options
  • Save topologicallytony/4e8b8ddc4c5776e6359f395042e16684 to your computer and use it in GitHub Desktop.
Save topologicallytony/4e8b8ddc4c5776e6359f395042e16684 to your computer and use it in GitHub Desktop.
Cantor Set

The Cantor Set is the set of points in [0,1] after carrying out the below steps (ad infinitum)

  1. Start with a line segment [0,1]
  2. Remove the open middle third of any connected line segments from the previous step
  3. Repeat 2.

The Cantor Set can also be thought of as any point in [0,1] that, when written in base 3 (ternary), does not contain the number 1. (i.e. any number whose ternary expansion consists of 0's and 2's).

The Cantor Set is an uncountably infinite set of points in the interval with Lebesgue measure 0.

More can be found here: wiki-Cantor Set

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cantor Set</title>
<!-- D3.js -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<style type="text/css">
</style>
</head>
<body>
<div>
Click to iterate
</div>
<div id="container">
<script type="text/javascript">
//Width and height of the visualization. Smaller numbers will zoom in, larger numbers zoom out
var w = 800;
var h = 400;
//padding creates a buffer of white space around the chart to make it a little easier to look at
var padding = 15;
//Generate Base Dataset
//Start with line from x = 0 t
//Create the scales used to map the set
var xScale = d3.scaleLinear()
.domain([0,1])
.range([padding, w - padding]);
//Create a canvas to display the chart on
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
//Create group elements to layer the svg
//This is an easy way to make sure things you want on top are on top, and things you want behind stay behind
var layer1 = svg.append('g');
var layer2 = svg.append('g');
//This function will iterate the cantor set and is called on mouse click
function step() {
//temp_dataset holds the most recently displayed iteration of the cantor set
temp_dataset = curr_dataset;
//curr_dataset will get loaded with the next iteration of the cantor set
curr_dataset = [];
//What iteration we are on (determined by the number of line segments, which will always be 2x the previous iteration)
var y = Math.log2(temp_dataset.length) + 1;
var len = temp_dataset.length;
//For each line segment, we remove the middle third and load the 2 new line segments into curr_dataset and dataset
for (var i = 0; i < len; i++) {
//get the endpoints of the current line segment
var low = temp_dataset[i][0];
var high = temp_dataset[i][1];
//get the endpoints of the new segment
var firstThird_x2 = (((high - low) / 3) + low);
var secondThird_x1 = (((high - low) * (2 / 3)) + low);
//store the new endpoints
curr_dataset.push([low, firstThird_x2, y]);
curr_dataset.push([secondThird_x1, high, y]);
dataset.push([low, firstThird_x2, y]);
dataset.push([secondThird_x1, high, y]);
}
//rescale the y-axis to accomidate the next iteration which will be plotted on the minimum of the y-axis
var yScale = d3.scaleLinear()
.domain([0,Math.log2(curr_dataset.length)])
.range([padding, h - padding]);
//For each line segment, draw the lines
//dataset holds all line segments, not just the current iteration
for (var i = 0; i < dataset.length; i++) {
var line = layer2.append("line")
.attr("x1", xScale(dataset[i][0]))
.attr("x2",xScale(dataset[i][1]))
.attr("y1",yScale(dataset[i][2]))
.attr("y2",yScale(dataset[i][2]))
.attr("stroke", "steelblue")
.attr("stroke-width", "1.5px")
.attr("fill", "none");
}
//Display the iteration number
var numIterations = layer1.selectAll(".iter")
.data([0]);
numIterations
.text(y);
numIterations.enter().append("text")
.attr("x", xScale(0))
.attr("y", yScale(0))
.text(y)
.attr("font-size", "12px")
.attr("class", "iter");
numIterations.exit().remove();
}
//initialize the datasets
var temp_dataset = [];
//dataset will hold all the line segments, not just the current iteration
//initialize with a line from 0 to 1
var dataset = [[0,1,0]];
var curr_dataset = dataset;
var yScale = d3.scaleLinear()
.domain([0,Math.log2(curr_dataset.length)])
.range([padding, h - padding]);
//Draw the initial line from 0 to 1
var line = layer2.append("line")
.attr("x1", xScale(dataset[0][0]))
.attr("x2",xScale(dataset[0][1]))
.attr("y1",yScale(dataset[0][2]))
.attr("y2",yScale(dataset[0][2]))
.attr("stroke", "steelblue")
.attr("stroke-width", "1.5px")
.attr("fill", "none");
//Handle click event by clearing all the lines and running step()
d3.select("body")
.on("click", function(){
svg.selectAll("line").remove();
step();
});
d3.select("body")
.on("touchstart", function(){
svg.selectAll("line").remove();
step();
});
</script>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment