Skip to content

Instantly share code, notes, and snippets.

@jameshenegan
Last active September 29, 2019 16:50
Show Gist options
  • Save jameshenegan/cf358f276fdeec3860d381bfe36e1e6a to your computer and use it in GitHub Desktop.
Save jameshenegan/cf358f276fdeec3860d381bfe36e1e6a to your computer and use it in GitHub Desktop.
Mandelbrot D3.js V5

Here we use random sampling to construct and draw (part of) the Mandelbrot set. We keep track of the following proportion: "number of points in set"/"number of points sampled"

Todo

  • Let the user "zoom in" after a certain number of epochs have passed.
  • Use some "fancy" statistics techniques so that, instead of sampling from uniform distributions, we are more selective/intelligent with our sampling.
var margin = { top: 10, right: 30, bottom: 30, left: 60 },
width = 400 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
const epochs = 150
const pointsPerEpoch = 2000
var data
var xMin = -1.5, xMax = 0.6, yMin = -1.05, yMax = 1.05
const xLength = xMax - xMin
const yLength = yMax - yMax
// append the svg object to the body of the page
var svg = d3.select("body")
.append("svg")
.attr("id", "canvas")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
makeChart(xMin, xMax, yMin, yMax)
function makeChart(xMin, xMax, yMin, yMax) {
// Add X axis
var xScale = d3.scaleLinear()
.domain([xMin, xMax])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// Add Y axis
var yScale = d3.scaleLinear()
.domain([yMin, yMax])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(yScale));
var uniformXshift = d3.scaleLinear().domain([0,1]).range([xMin, xMax])
var uniformYshift = d3.scaleLinear().domain([0,1]).range([yMin, yMax])
const clockCounter = d3.timer(addPoint, 1000)
var currentEpoch = 0
var pointsTried = 0
var pointSuccess = 0
function addPoint() {
currentEpoch = currentEpoch + 1
d3.select("#epochNum").text(currentEpoch)
if (currentEpoch > epochs) {
clockCounter.stop()
}
const numPoints = pointsPerEpoch
var outerCounter = 0
console.log()
while (outerCounter < numPoints) {
outerCounter = outerCounter + 1
// shift the interval [0,1] to [xMin, xMax]
const u = uniformXshift(Math.random()), v = uniformYshift(Math.random())
var x, y, reW = 0, imW = 0, modW
var counter = 0, notFailedYet = true
while (counter < 100 && notFailedYet) {
counter = counter + 1
x = reW
y = imW
reW = x * x - y * y + u
imW = 2 * x * y + v
modW = reW * reW + imW * imW
if (modW > 4) {
notFailedYet = false
}
}
data = [{ x: u, y: v }]
if (notFailedYet) {
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return xScale(d.x); })
.attr("cy", function (d) { return yScale(d.y); })
.attr("r", 1)
.style("fill", "#69b3a2")
pointSuccess = pointSuccess + 1
}
}
pointsTried = pointsTried + pointsPerEpoch
d3.select("#proportion").text(Math.round(10000*pointSuccess/pointsTried)/10000)
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<h4>Epoch Number : <span id = "epochNum"></span> </h4>
<h4>Proportion : <span id = "proportion"></span> </h4>
<script src="canvas.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment