Snell's law
From JSXGraph Wiki
Refraction of a light ray emanating from the source L at the interface between two environments of different refractive indices, n1, n2.
Reference: https://en.wikipedia.org/wiki/Snell%27s_law. Construction by by Roman Hašek.
The complete JavaScript code
var board = JXG.JSXGraph.initBoard('box', {boundingbox: [-5, 5, 5, -5], axis:false});
// Line l1 as an interface between two environments, green, with the index of refraction
// n_1, and the blue, with the index of refraction n_2.
var M = board.create('point',[-4,0],{name:'M', visible:false, fixed:true});
var I = board.create('point',[0,0],{name:'I', size:1, fixed:true});
var l1 = board.create('line', [M,I]);
ineq1 = board.create('inequality', [l1], {fillColor: 'green'});
ineq2 = board.create('inequality', [l1], {inverse: true, fillColor: 'blue'});
// Normal line n with auxiliary points N and O that allows us to determine
// the angles of incidence (α) and refraction (β), respectively
var n = board.create('perpendicular', [l1,I], {name:'n', color: 'black', dash:"2", strokeWidth:1});
var N = board.create('glider',[0,4,n], {name:'N', visible:false});
var O = board.create('glider',[0,-4,n], {name:'O', visible:false});
// a light source L
var L = board.create('point', [-3,4], {name:'L', color:'red', size:3});
// Position of the light source L is limited to the green environment
var xL, yL;
L.on('drag', function() {
if(L.Y() < 0 ) {
L.moveTo([xL,yL],0);
}
xL = L.X(); yL = L.Y();
});
// r1, the incident light ray
var r1 = board.create('segment', [L, I], {strokeColor:'orange', strokeWidth:4});
// Sliders to control indexes of refraction
var n_1 = board.create('slider', [[-4, -3], [-2,-3], [1, 1, 3]], {name:'n_1', snapWidth: 0.01});
var n_2 = board.create('slider', [[-4, -4], [-2,-4], [1, 1, 3]], {name:'n_2', snapWidth: 0.01});
// The value of s controls the kind of refraction/reflection, if s > 1 the total reflection occurs
// (numerically it is the absolute value of the sine of the angle of refraction)
var s = function() { return (n_1.Value()/n_2.Value())*Math.abs(Math.sin(JXG.Math.Geometry.angle(N,I,L))).toFixed(6); }
// Two possible points through which the modified ray passes, B for the reflected ray and C for the refracted one
var B = board.create('point', [
function(){ return -L.X(); },
function(){ return L.Y(); }
], {
visible: function(){
return (s()>1 || Math.abs(Math.sin(JXG.Math.Geometry.angle(N,I,L)))==1)? true : false;
}, name:'R_1', face:'o', size:1, visible: false
});
var C = board.create('point', [
function(){ return 5*(n_1.Value()/n_2.Value())*Math.sin(JXG.Math.Geometry.angle(N,I,L)); },
function(){
return -5*Math.cos(Math.asin((n_1.Value()/n_2.Value()) * Math.sin(JXG.Math.Geometry.angle(N,I,L)))); }
], {
visible: function(){
return (s()<=1 && Math.abs(Math.sin(JXG.Math.Geometry.angle(N,I,L)))!=1)? true : false;
}, name:'R_2', face:'o', size:1, visible:false
});
// Reflected (r2) and refracted (r3) ray
var r2 = board.create('segment', [I, B], {
visible: function(){
return (s()>1 || Math.abs(Math.sin(JXG.Math.Geometry.angle(N,I,L)))==1)? true : false;
}, strokeColor:'orange', strokeWidth:4, lastArrow: {type: 1, size: 3}
});
var r3 = board.create('segment', [I, C], {
visible: function(){
return (s()<=1 && Math.abs(Math.sin(JXG.Math.Geometry.angle(N,I,L)))!=1)? true : false;
}, strokeColor:'orange', strokeWidth:4, lastArrow: {type: 1, size: 3}
});
// Angles of impact (angle 1), refraction (angle2) and reflection (angle3), respectively
var angle1 = board.create('nonreflexangle',[N, I, L], {radius:1,color:'orange', fillOpacity: 0, name: 'α'});
var angle2 = board.create('nonreflexangle',[O,I,C], {
visible: function(){
return (s()<=1 && Math.abs(Math.sin(JXG.Math.Geometry.angle(N,I,L)))!=1)? true : false;
}, radius:1, color:'orange', fillOpacity: 0, name: 'β'
});
var angle3 = board.create('nonreflexangle',[B,I,N], {
visible:function(){
return (s()>1 || Math.abs(Math.sin(JXG.Math.Geometry.angle(N,I,L)))==1)? true : false;
}, radius:1, color:'orange', fillOpacity: 0, name: 'β'
});