|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<title></title> |
|
<style> |
|
p { |
|
font: 14px sans-serif; |
|
} |
|
|
|
#left { |
|
float: left; |
|
width: 50%; |
|
text-align: right; |
|
} |
|
|
|
#right { |
|
float: right; |
|
width: 50%; |
|
text-align: left; |
|
} |
|
|
|
#info { |
|
margin-left: 50px; |
|
} |
|
|
|
#canvas-element { |
|
border: solid 1px black; |
|
} |
|
|
|
.axis path, |
|
.axis line { |
|
fill: none; |
|
stroke: #000; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.axis text { |
|
font: 12px sans-serif; |
|
} |
|
|
|
.line { |
|
fill: none; |
|
stroke: #218380; |
|
stroke-width: 1px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div id="container"> |
|
<div id="left"> |
|
<canvas id="canvas-element" class="plot"></canvas> |
|
</div> |
|
<div id="right"> |
|
<svg id="svg-element"></svg> |
|
<div id="info"> |
|
<p>Total Iterations: <span id="total-iteration"></span></p> |
|
<p>Total Number of points in circle: <span id="in-circle"></span></p> |
|
<p>Current estimate value of Pi: <span id="pi-estimate"></span></p> |
|
<p><button id="start-button">Start</button></p> |
|
</div> |
|
</div> |
|
</div> |
|
</body> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.min.js" charset="utf-8"></script> |
|
<script> |
|
var circleWidth = 400; |
|
var circleHeight = 400; |
|
var circleRadius = Math.min(circleHeight, circleWidth) / 2; |
|
var canvasArea = circleWidth * circleHeight; |
|
|
|
var margin = {top: 10, right: 20, bottom: 40, left: 60}; |
|
var lineWidth = 400 - margin.left - margin.right; |
|
var lineHeight = 250 - margin.top - margin.bottom; |
|
|
|
var totalInCircle = 0; |
|
var totalPoints = 2500; |
|
var radius = 4; |
|
var drawIntervalTime = 1; |
|
|
|
var totalPointsDrawn = 0; |
|
var pointsInCircle = 0; |
|
var estimatedPi = 0; |
|
var currentError = 0; |
|
|
|
var lineData = []; |
|
|
|
// create the canvas for drawing the circle |
|
var canvas = d3.select("#canvas-element") |
|
.attr("width", circleWidth) |
|
.attr("height", circleHeight); |
|
|
|
var context = canvas.node().getContext("2d"); |
|
|
|
context.strokeStyle = 'black'; |
|
context.lineWidth = 3; |
|
context.beginPath(); |
|
context.arc(circleWidth/2, circleHeight/2, circleRadius, 0, 2 * Math.PI); |
|
context.closePath(); |
|
context.stroke(); |
|
|
|
// create the line plot |
|
var svg = d3.select("#svg-element") |
|
.attr("width", lineWidth + margin.left + margin.right) |
|
.attr("height", lineHeight + margin.top + margin.bottom) |
|
.append("g") |
|
.attr("transform", translateString(margin.left, margin.top)); |
|
|
|
var xScale = d3.scale.linear() |
|
.domain([0, totalPoints]) |
|
.range([0, lineWidth]); |
|
|
|
var yScale = d3.scale.linear() |
|
.domain([-0.4, 0.4]) |
|
.range([lineHeight, 0]) |
|
.clamp(true); |
|
|
|
var xAxis = d3.svg.axis() |
|
.scale(xScale) |
|
.ticks(7) |
|
.orient("bottom"); |
|
|
|
var yAxis = d3.svg.axis() |
|
.scale(yScale) |
|
.orient("left"); |
|
|
|
// append line along x = 0 |
|
svg.append("g") |
|
.attr("class", "axis") |
|
.append("line") |
|
.attr("x1", 0) |
|
.attr("y1", yScale(0)) |
|
.attr("x2", lineWidth) |
|
.attr("y2", yScale(0)); |
|
|
|
// append axis labels |
|
svg.append("g") |
|
.attr("class", "axis") |
|
.append("text") |
|
.attr("text-anchor", "middle") |
|
.attr("x", (lineWidth) / 2) |
|
.attr("y", (lineHeight + margin.top + margin.bottom) - 10) |
|
.text("Number of Interations"); |
|
|
|
svg.append("g") |
|
.attr("class", "axis") |
|
.append("text") |
|
.attr("transform", "rotate(-90)") |
|
.attr("y", -margin.left + 25) |
|
.attr("x", -lineHeight / 2) |
|
.style("text-anchor", "middle") |
|
.text("Error"); |
|
|
|
|
|
var lineFunction = d3.svg.line() |
|
.x(function(d) { return d[0] }) |
|
.y(function(d) { return d[1] }); |
|
|
|
// create currently empty line |
|
var lineSvg = svg.append("path") |
|
.attr("class", "line") |
|
.attr("d", lineFunction(lineData)); |
|
|
|
svg.append("g") |
|
.attr("class", "axis") |
|
.attr("transform", translateString(0, lineHeight)) |
|
.call(xAxis); |
|
|
|
svg.append("g") |
|
.attr("class", "axis") |
|
.call(yAxis); |
|
|
|
d3.select("#start-button").on("click", start); |
|
|
|
// start the show! |
|
function start() { |
|
var count = 0; |
|
var drawInterval = setInterval(function() { |
|
if(count > totalPoints) { |
|
clearInterval(drawInterval); |
|
} |
|
update(count); |
|
count++; |
|
}, 10); |
|
|
|
// prevent from starting twice |
|
d3.select("#start-button").on("click", null); |
|
} |
|
|
|
function update(count) { |
|
// generate new random x/y pair |
|
var x = randomFloat(0, circleWidth); |
|
var y = randomFloat(0, circleHeight); |
|
|
|
// check if the point is in the circle |
|
if(euclideanDistance(x, circleWidth / 2, y, circleHeight / 2) < circleRadius) { |
|
context.fillStyle = '#73D2DE'; |
|
totalInCircle++; |
|
} |
|
else { |
|
context.fillStyle = '#D81159'; |
|
} |
|
|
|
// draw thw point |
|
drawPoint(x, y, radius); |
|
|
|
totalPointsDrawn++; |
|
estimatedPi = (totalInCircle / totalPointsDrawn) * 4; |
|
currentError = Math.PI - estimatedPi; |
|
|
|
// update the lineplot |
|
// only do this every 10th iteration to reduce noise in the lineplot |
|
if(count % 10 === 0) { |
|
lineData.push([xScale(count), yScale(currentError)]); |
|
lineSvg.attr("d", lineFunction(lineData)); |
|
} |
|
|
|
updateText(); |
|
} |
|
|
|
function updateText() { |
|
document.getElementById('total-iteration').innerHTML = totalPointsDrawn.toString(); |
|
document.getElementById('in-circle').innerHTML = totalInCircle.toString(); |
|
document.getElementById('pi-estimate').innerHTML = estimatedPi.toString(); |
|
} |
|
|
|
function drawPoint(x, y, r) { |
|
context.beginPath(); |
|
context.arc(x, y, r, 0, 2 * Math.PI); |
|
context.closePath(); |
|
context.fill(); |
|
} |
|
|
|
function translateString(x, y) { |
|
var out = []; |
|
out.push("translate("); |
|
out.push(x); |
|
out.push(" "); |
|
out.push(y); |
|
out.push(")"); |
|
return out.join(""); |
|
} |
|
|
|
function rotateString(r) { |
|
var out = []; |
|
out.push("rotate("); |
|
out.push(r); |
|
out.push(")"); |
|
return out.join(""); |
|
} |
|
|
|
function euclideanDistance(x1, x2, y1, y2) { |
|
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); |
|
} |
|
|
|
function randomFloat(min, max) { |
|
return (max - min) * Math.random() + min; |
|
} |
|
</script> |
|
</html> |