|
<!DOCTYPE html> |
|
|
|
<meta charset="utf-8"> |
|
<meta name="author" content="Brandon Seah"> |
|
<head> |
|
|
|
</head> |
|
|
|
<body> |
|
<div> |
|
<button onclick="toggleOffset()">Toggle Filter</button> |
|
<button onclick="changeSpeed(2)">Speed up pendulum</button> |
|
<button onclick="changeSpeed(0.5)">Slow down pendulum</button> |
|
<button onclick="changeOffset(1.5)">Darken filter</button> |
|
<button onclick="changeOffset(0.75)">Lighten filter</button> |
|
|
|
<svg class="stereo"></svg> |
|
</div> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
// DEMO 1.5 - Stereoscopic lines of sight |
|
|
|
// Define chart dimensions |
|
var height=400; |
|
var width=800; |
|
|
|
// Starting parameters |
|
var timeStereo=0; |
|
var speedParamStereo=0.05; |
|
var initX=0; // Should be either zero or width |
|
var planeY=100; // Position of image plane |
|
var eyesPlaneY=350; // Position of eyes plane |
|
var eyeLeftX = 300; |
|
var eyeRightX = 500; |
|
|
|
// Render chart and background |
|
var chartStereo = d3.select(".stereo") |
|
.attr("height",height) |
|
.attr("width",width) |
|
.append("g"); |
|
// Background color light yellow |
|
chartStereo.append("rect") |
|
.attr("height",height) |
|
.attr("width",width) |
|
.attr("fill","#feffe7"); |
|
// Line to represent plane of pendulum |
|
chartStereo.append("path") |
|
.attr("d","M0 "+planeY+" H "+width) |
|
.attr("fill","transparent") |
|
.attr("stroke","black"); |
|
// Text label for pendulum plane |
|
chartStereo.append("text") |
|
.attr("x",10) |
|
.attr("y",planeY-10) |
|
.text("Actual plane of motion") |
|
.attr("font-family","sans-serif") |
|
.attr("font-size","18px") |
|
.attr("fill","black"); |
|
// Pendulum |
|
chartStereo.append("circle") |
|
.attr("class","pend1") |
|
.attr("r",10) |
|
.attr("cx",initX) |
|
.attr("cy",planeY) |
|
.attr("fill","blue") |
|
.attr("opacity",1); |
|
// "shadow" pendulum with delay |
|
chartStereo.append("circle") |
|
.attr("class","pend2") |
|
.attr("r",10) |
|
.attr("cx",initX) |
|
.attr("cy",planeY) |
|
.attr("fill","grey") |
|
.attr("opacity",0.5); |
|
// Line to represent plane of eyes |
|
chartStereo.append("path") |
|
.attr("d","M0 "+eyesPlaneY+" H "+width) |
|
.attr("fill","transparent") |
|
.attr("stroke","black"); |
|
// Text label for eyes plane |
|
chartStereo.append("text") |
|
.attr("x",10) |
|
.attr("y",eyesPlaneY+25) |
|
.text("Position of eyes") |
|
.attr("font-family","sans-serif") |
|
.attr("font-size","18px") |
|
.attr("fill","black"); |
|
// Left eye sightline |
|
chartStereo.append("path") |
|
.attr("class","leftEye") |
|
.attr("d","M"+eyeLeftX+" "+eyesPlaneY+" L"+initX+" "+planeY) |
|
.attr("fill","transparent") |
|
.attr("stroke","blue"); |
|
// Right eye sightline |
|
chartStereo.append("path") |
|
.attr("class","rightEye") |
|
.attr("d","M"+eyeRightX+" "+eyesPlaneY+" L"+initX+" "+planeY) |
|
.attr("fill","transparent") |
|
.attr("stroke","grey"); |
|
// Left eye pic |
|
chartStereo.append("circle") |
|
.attr("class","eye") |
|
.attr("cx",eyeLeftX) |
|
.attr("cy",eyesPlaneY+(height-eyesPlaneY)/2) |
|
.attr("r",(height-eyesPlaneY)/2) |
|
.attr("stroke","black") |
|
.attr("fill","white"); |
|
// Right eye pic |
|
chartStereo.append("circle") |
|
.attr("class","eye") |
|
.attr("cx",eyeRightX) |
|
.attr("cy",eyesPlaneY+(height-eyesPlaneY)/2) |
|
.attr("r",(height-eyesPlaneY)/2) |
|
.attr("stroke","black") |
|
.attr("fill","white"); |
|
// Right eye label |
|
chartStereo.append("text") |
|
.attr("class","filterText") |
|
.attr("x",eyeRightX+30) |
|
.attr("y",eyesPlaneY+25) |
|
.text("Right eye sees delayed image") |
|
.attr("font-family","sans-serif") |
|
.attr("font-size","14px") |
|
.attr("fill","black"); |
|
// Filter pic |
|
filterOpacity=0.5; |
|
chartStereo.append("rect") |
|
.attr("class","filter") |
|
.attr("x",eyeRightX-50) |
|
.attr("y",eyesPlaneY-20) |
|
.attr("height",5) |
|
.attr("width",100) |
|
.attr("fill","black") |
|
.attr("opacity",filterOpacity); |
|
// Filter label |
|
chartStereo.append("text") |
|
.attr("class","filterText") |
|
.attr("x",eyeRightX+60) |
|
.attr("y",eyesPlaneY-12) |
|
.text("Filter") |
|
.attr("font-family","sans-serif") |
|
.attr("font-size","14px") |
|
.attr("fill","black"); |
|
|
|
// Visual offset introduced by the filter |
|
var offsetReset = 2; |
|
var offset=offsetReset; |
|
|
|
// Function to update image in each step |
|
function updateStereo() { |
|
// Calculate new positions of the pendulum and its "shadow" |
|
var newPend1X = width*(1+Math.cos(timeStereo/Math.PI))/2; |
|
var newPend2X = width*(1+Math.cos((timeStereo/Math.PI)-offset*speedParamStereo))/2; |
|
chartStereo.select(".pend1") |
|
.attr("cx",newPend1X); |
|
chartStereo.select(".pend2") |
|
.attr("cx",newPend2X); |
|
// Calculate endpoints of sightlines with similar triangles |
|
var newPendEnd1X = eyeLeftX - (newPend1X-eyeLeftX)*(eyesPlaneY/(planeY-eyesPlaneY)); |
|
var newPendEnd2X = eyeRightX - (newPend2X-eyeRightX)*(eyesPlaneY/(planeY-eyesPlaneY)); |
|
chartStereo.select(".leftEye") |
|
.attr("d","M"+eyeLeftX+" "+eyesPlaneY+" L"+newPendEnd1X+" "+0); |
|
chartStereo.select(".rightEye") |
|
.attr("d","M"+eyeRightX+" "+eyesPlaneY+" L"+newPendEnd2X+" "+0); |
|
} |
|
|
|
function changeSpeed(factor) { |
|
speedParamStereo = speedParamStereo*factor; |
|
} |
|
|
|
function changeOffset(factor) { |
|
offsetReset = offsetReset*factor; |
|
offset = offsetReset; |
|
// Change opacity of filter - need a function which has horizontal asymptote at 1, |
|
// range (0,1) and domain (0,Inf). |
|
filterOpacity = filterOpacity + (2/Math.PI)*(Math.atan(Math.log(factor))); |
|
chartStereo.selectAll(".filter") |
|
.attr("opacity",filterOpacity); |
|
} |
|
|
|
// Function to change visual offset |
|
function toggleOffset() { |
|
if (offset > 0) { |
|
// Reposition delayed pendulum |
|
offset = 0; |
|
// Recolor delayed pendulum and sightline |
|
chartStereo.select(".pend2") |
|
.attr("fill","blue"); |
|
chartStereo.select(".rightEye") |
|
.attr("stroke","blue"); |
|
chartStereo.selectAll(".filter") |
|
.attr("display","none"); |
|
chartStereo.selectAll(".filterText") |
|
.attr("display","none"); |
|
} else if (offset == 0) { |
|
// Return delayed virtual pendulum |
|
offset = offsetReset; |
|
// Recolor delayed pendulum and sightline to grey |
|
chartStereo.select(".pend2") |
|
.attr("fill","grey"); |
|
chartStereo.select(".rightEye") |
|
.attr("stroke","grey"); |
|
chartStereo.selectAll(".filter") |
|
.attr("display","unset"); |
|
chartStereo.selectAll(".filterText") |
|
.attr("display","unset"); |
|
} |
|
} |
|
|
|
// Timer function to render animation |
|
setInterval( function() { |
|
timeStereo = timeStereo + speedParamStereo; |
|
return updateStereo(); |
|
}, 10); |
|
</script> |
|
</body> |