Refraction of Light
Physics
Code
chart = {
const margin = { top: 20, right: 10, bottom: 20, left: 10 };
const width = fullWidth - margin.left - margin.right,
height = fullHeight - margin.top - margin.bottom;
const window = d3.create("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${fullWidth} ${fullHeight}`)
.attr("preserveAspectRatio", "xMidYMid meet");
const svg = window.append("g")
.attr("class", "refrac")
.attr("transform", `translate(${margin.left},${margin.top})`);
const line = d3.line();
let state = {
_n: defaults.n,
_a: defaults.a,
_pol: defaults.pol,
get n() {
return this._n;
},
set n(x) {
this._n = x;
this.update();
},
get a() {
return this._a;
},
set a(x) {
this._a = x;
this.update();
},
get pol() {
return this._pol;
},
set pol(x) {
this._pol = x;
this.update();
},
get aT() {
return Math.asin(this.n);
},
get aB() {
return Math.atan(this.n);
},
update: function() {
this.b = Math.asin(Math.sin(this.a) / this.n);
// Snell's Law
this.deflection = {
t: this.a - this.b,
r: Math.PI + 2 * this.a
};
// Fresnel's Equations
this.intensity = {
s: {
t: Math.pow(2 * Math.cos(this.a)
/ (Math.cos(this.a) + this.n * Math.cos(this.b)), 2),
r: Math.pow((Math.cos(this.a) - this.n * Math.cos(this.b))
/ (Math.cos(this.a) + this.n * Math.cos(this.b)), 2),
},
p: {
t: Math.pow(2 * Math.cos(this.a)
/ (Math.cos(this.b) + this.n * Math.cos(this.a)), 2),
r: Math.pow((this.n * Math.cos(this.a) - Math.cos(this.b))
/ (Math.cos(this.b) + this.n * Math.cos(this.a)), 2),
}
};
}
};
// set up defaults
state.update();
const radius = width / 2,
origin = [0, height / 2],
pivot = [origin[0] + radius, origin[1]];
svg.selectAll(".ray")
.data([[origin], [pivot], [pivot]])
.join("path")
.attr("class", (d, i) => `ray ray${i}`);
svg.append("path")
.attr("class", "surface");
function updateData() {
// input ray
svg.select(".ray0").datum([origin, pivot]);
// transmitted ray
if (state.n < 1 && Math.abs(state.a) >= state.aT) {
state.intensity[state.pol].r = 1; // could be Nan
svg.select(".ray1").datum([pivot]);
} else {
svg.select(".ray1").datum([pivot, [
pivot[0] + radius * Math.cos(state.deflection.t),
pivot[1] + radius * Math.sin(state.deflection.t),
]]);
}
// reflected ray
svg.select(".ray2").datum([pivot, [
pivot[0] + radius * Math.cos(state.deflection.r),
pivot[1] + radius * Math.sin(state.deflection.r),
]]);
}
function updateChart() {
svg.select(".surface")
.attr("d", line([
[
radius * Math.sin(state.a) + pivot[0],
-radius * Math.cos(state.a) + pivot[1],
], [
-radius * Math.sin(state.a) + pivot[0],
radius * Math.cos(state.a) + pivot[1],
],
]));
// input ray
svg.select(".ray0")
.attr("d", line)
.attr("opacity", 1);
// transmitted ray
svg.select(".ray1")
.attr("d", line)
.attr("opacity", state.intensity[state.pol].t);
// reflected ray
svg.select(".ray2")
.attr("d", line)
.attr("opacity", state.intensity[state.pol].r);
setInputDisabled(viewof _onTotal, state.n >= 1);
setInputDisabled(viewof _onZero, state.pol == "s");
}
function updateAll() {
updateData();
updateChart();
}
invalidation.then(() => {
svg.selectAll("*").interrupt();
});
function rotationTween(a) {
return function() {
const interpolate = d3.interpolateNumber(state.a, a);
return function(t) {
state.a = interpolate(t);
updateAll();
setInputValue(viewof _a, state.a);
};
}
}
return Object.assign(window.node(), {
update(values) {
state.a = values.a;
state.n = values.n;
state.pol = values.pol;
updateAll();
},
setTotal(value) {
if (value > 0) {
svg.transition()
.duration(500)
.tween("rotate", rotationTween(state.aT));
}
},
setZero(value) {
if (value > 0) {
svg.transition()
.duration(500)
.tween("rotate", rotationTween(state.aB));
}
},
});
}This interactive visualization demonstrates the reflection and refraction of a light beam at the interface of a medium, as described by Snell’s law and Fresnel’s equations. Depending on the value of the refractive index and the polarization, the incoming beam can be fully reflected or fully refracted.