Files
WavesPrograms/rainbow2.html
2021-08-19 17:45:00 -07:00

343 lines
12 KiB
HTML

<!--
This work is licensed under CC BY-NC-ND 4.0
Link to license: http://creativecommons.org/licenses/by-nc-nd/4.0/
Attribute to Russell Georgi
-->
<html>
<head>
<title>
Waves: Rainbow 2
</title>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
display: block;
}
canvas {
position: absolute;
}
form {
position: relative;
}
.slideContainer {
position: relative;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="1" height="1" style="border:1px solid #ffffff;">
Your browser does not support the HTML5 canvas tag.</canvas>
<form>
<label for="red">Red</label>
<input type="checkbox" id="red" onclick="redClick()">
<br>
<label for="blue">Blue</label>
<input type="checkbox" id="blue" onclick="blueClick()">
</form>
<div class="slidecontainer">
<t>Ray density</t>
<input type="range" min="0.09" max="0.2" value="0.14" step = "0.003" id="slider">
</div>
<script>
class Vector2 {
constructor (x, y) {
this.x = x;
this.y = y;
}
scale(n)
{
this.x *= n;
this.y *= n;
}
get len()
{
return this.calcLen();
}
calcLen()
{
return (Math.sqrt(this.x * this.x + this.y * this.y));
}
}
class Circle {
constructor (x, y, r)
{
this.x = x;
this.y = y;
this.r = r;
}
}
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
var nn = 1.35;
var r = 25;
var cl4 = "#ee0000";
var cl17 = "#9999ff";
var cl15 = "#000000";
var red = false;
var blue = false;
var dtog = 0;
var x0 = 0;
var y0 = 60;
var x1 = 0;
var y1 = -60;
var dy = 10;
var nnn = 50;
var maxSteps = 18000;
var reflections = 0;
var maxReflections = 1;
var step = r / 10;
var xPos = 0;
var yPos = 0;
var red = false;
var blue = false;
var density = 0.14;
var circles = [];
slider.oninput = function()
{
density = parseFloat(this.value);
console.log(density);
Update();
}
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.scale(1.5, -1.5);
function Clear(ctx)
{
ctx.clearRect(-c.width, -c.height, c.width * 2, c.height * 2);
}
function Update()
{
Clear(ctx);
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.arc(xPos + x0, yPos + y0, r, 0, Math.PI * 2);
ctx.stroke();
circles.push(new Circle(x0, y0, r));
ctx.beginPath();
ctx.arc(xPos + x1, yPos + y1, r, 0, Math.PI * 2);
ctx.stroke();
ctx.scale(1, -1);
ctx.fillText("Light from one reflection", -150, -30);
ctx.fillText("Light from two reflections", -50, 130);
ctx.fillText("Dark band", -155, 140);
ctx.scale(1, -1);
circles.push(new Circle(x1, y1, r));
for (h2 = 0; h2 <= 1; h2 += density)
{
if (h2 != 0 && h2 < 0.99999)
{
if (red)
{
maxReflections = 1;
Ray(new Vector2(1 - c.width, h2 * r + y0), new Vector2(1, 0), 1, 1.332, "#ee0000");
maxReflections = 2;
Ray(new Vector2(1 - c.width, - h2 * r + y1), new Vector2(1, 0), 1, 1.332, "#ee0000");
}
if (blue)
{
maxReflections = 1;
Ray(new Vector2(1 - c.width, h2 * r + y0), new Vector2(1, 0), 1, 1.35, "#9999ff");
maxReflections = 2;
Ray(new Vector2(1 - c.width, - h2 * r + y1), new Vector2(1, 0), 1, 1.35, "#9999ff");
}
}
}
/*Ray(new Vector2(1 - c.width, 2 * r / 3 + y0), new Vector2(1, 0.00), 1, 1.332, "#ee0000");
//Ray(new Vector2(r / 2, c.height - 1), new Vector2(-0.03, -1), 0.1, 1.332, "#ee0000");
Ray(new Vector2(1 - c.width, 2 * r / 3 + y0), new Vector2(1, 0.00), 1, 1.35, "#9999ff");
maxReflections = 2;
Ray(new Vector2(1 - c.width, - 15 * r / 16 + y1), new Vector2(1, 0.00), 1, 1.332, "#ee0000");
//Ray(new Vector2(r / 2, c.height - 1), new Vector2(-0.03, -1), 0.1, 1.332, "#ee0000");
Ray(new Vector2(1 - c.width, - 15 * r / 16 + y1), new Vector2(1, 0.00), 1, 1.35, "#9999ff");
//setTimeout(Update, 1000/60);*/
}
function Ray(startPos, startDir, distStep, nCircle, color)
{
pos = startPos;
dir = startDir;
n = 1;
nLastStep = 1;
nSteps = 0;
reflections = 0;
circlePos = new Vector2(0, 0);
for (i = 0; i < circles.length; i++)
{
dist = Math.sqrt(Math.pow(pos.x - circles[i].x, 2) + Math.pow(pos.y - circles[i].y, 2));
if (dist < r)
{
n = nCircle;
circlePos = new Vector2(circles[i].x, circles[i].y);
}
}
while (Math.abs(pos.x) <= c.width && Math.abs(pos.y) <= c.height && nSteps <= maxSteps)
{
nSteps += 1;
nCurrent = 1;
for (i = 0; i < circles.length; i++)
{
dist = Math.sqrt(Math.pow(pos.x - circles[i].x, 2) + Math.pow(pos.y - circles[i].y, 2));
if (dist < r)
{
nCurrent = nCircle;
circlePos = new Vector2(circles[i].x, circles[i].y);
}
}
if (nCurrent != n && nCurrent != nLastStep)
{
if (nCurrent < n && reflections < maxReflections)
{
dir = calculateReflectedDir(dir, pos, n, nCurrent, circlePos);
reflections += 1;
} else
{
dir = calculateRefractedDir(dir, pos, n, nCurrent, circlePos);
}
//console.log(dir, nCurrent, nSteps);
}
nLastStep = n;
n = nCurrent;
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
ctx.lineTo(pos.x + dir.x * distStep, pos.y + dir.y * distStep);
ctx.stroke();
pos.x += dir.x * distStep;
pos.y += dir.y * distStep;
}
}
function calculateRefractedDir(dir, pos, n1, n2, cPos)
{
//console.log("recalc");
posToCircle = new Vector2(pos.x - cPos.x, pos.y - cPos.y);
circleNormal = new Vector2(-posToCircle.x / posToCircle.len, -posToCircle.y / posToCircle.len);
/*ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(pos.x + circleNormal.x * 10, pos.y + circleNormal.y * 10);
ctx.lineTo(pos.x - circleNormal.x * 10, pos.y - circleNormal.y * 10);
ctx.stroke();*/
if (n1 > n2)
{
circleNormal.x *= -1;
circleNormal.y *= -1;
}
theta1 = Math.acos((dir.x * circleNormal.x + dir.y * circleNormal.y) / (dir.len * circleNormal.len));
theta2 = Math.asin(n1 * Math.sin(theta1) / n2);
angle = theta2 - theta1;
angleDiff = polarCoords(dir).y - polarCoords(new Vector2(circleNormal.x, circleNormal.y)).y;
//console.log("calculate", angleDiff);
if (Math.abs(angleDiff) > 90)
{
angleDiff *= -1;
}
if (angleDiff < 0)
{
//console.log("flipr")
angle *= -1;
}
//console.log(angle, dir.x, dir.y, posToCircle.x, posToCircle.y, theta1, theta2);
return new Vector2(dir.x * Math.cos(angle) - dir.y * Math.sin(angle), dir.x * Math.sin(angle) + dir.y * Math.cos(angle));
}
function calculateReflectedDir(dir, pos, n1, n2, cPos)
{
//console.log("reflect");
posToCircle = new Vector2(pos.x - cPos.x, pos.y - cPos.y);
circleNormal = new Vector2(-posToCircle.x / posToCircle.len, -posToCircle.y / posToCircle.len);
ctx.strokeStyle = "#000000";
/*ctx.beginPath();
ctx.moveTo(pos.x + circleNormal.x * 10, pos.y + circleNormal.y * 10);
ctx.lineTo(pos.x - circleNormal.x * 10, pos.y - circleNormal.y * 10);
ctx.stroke();*/
if (n1 > n2)
{
circleNormal.x *= -1;
circleNormal.y *= -1;
}
theta1 = Math.acos((dir.x * circleNormal.x + dir.y * circleNormal.y) / (dir.len * circleNormal.len));
angle = Math.PI - 2 * theta1
angleDiff = polarCoords(dir).y - polarCoords(new Vector2(circleNormal.x, circleNormal.y)).y;
//console.log("calculate", angleDiff);
if (Math.abs(angleDiff) > 90)
{
angleDiff *= -1;
}
if (angleDiff < 0)
{
//console.log("flipr")
angle *= -1;
}
//console.log(angle, dir.x, dir.y, posToCircle.x, posToCircle.y);
return new Vector2(dir.x * Math.cos(angle) - dir.y * Math.sin(angle), dir.x * Math.sin(angle) + dir.y * Math.cos(angle));
}
window.addEventListener('resize', function(event) {
c.width = window.innerWidth;
c.height = window.innerHeight;
ctx.translate(c.width / 2, c.height / 2);
ctx.scale(1.5, -1.5);
Update();
}, true);
function polarCoords(pos)
{
theta = Math.atan(pos.y / pos.x);
if (pos.x < 0)
{
theta += Math.PI;
}
if (theta < 0)
{
theta += Math.PI * 2;
}
return new Vector2(pos.len, theta * 180 / Math.PI);
}
function redClick()
{
checkBox = document.getElementById("red");
red = checkBox.checked;
Clear(ctx);
Update();
}
function blueClick()
{
checkBox = document.getElementById("blue");
blue = checkBox.checked;
Clear(ctx);
Update();
}
Update();
</script>
</body>
<p xmlns:cc="http://creativecommons.org/ns#" style="font-size: 1vw; bottom: 0px; position: absolute;">
This work is licensed under
<a href="http://creativecommons.org/licenses/by-nc-nd/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY-NC-ND 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1"><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nd.svg?ref=chooser-v1"></a></p>
</html>