I am making a 2d light ray simulator with html canvas and i was creating a 1 pixel ImageData every time a ray moved so when i made it so it only created one at the start, it stopped working normally. The top is the old version that works and the bottom is the new version that doesn't work. I expect it to do the same thing but in the new version the collisions are messed up.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
var mouseX = 0;
var mouseY = 0;
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.fillStyle = "rgb(10, 10, 10)";
ctx.strokeStyle = "rgba(255, 255, 0)";
ctx.lineWidth = 3;
ImageData.prototype.getPixel = function(x, y, offset) {
return this.data[(y * this.width + x) * 4 + offset];
};
function drawLine(first, second) {
ctx.beginPath();
ctx.moveTo(first[0], first[1]);
ctx.lineTo(second[0], second[1]);
ctx.stroke();
};
function mousemove(e) {
mouseX = e.clientX;
mouseY = e.clientY;
};
function drawScene() {
ctx.fillStyle = "rgba(50, 50, 50, 1)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgba(10, 10, 10, 1)";
ctx.fillRect(0, canvas.height-10, 200, 10);
ctx.fillRect(0, canvas.height-210, 10, 200);
ctx.fillRect(10, canvas.height-210, 200, 10);
ctx.fillRect(200, canvas.height-190, 10, canvas.height-190);
};
drawScene();
function render() {
drawScene();
let rays = [];
var data;
for (let angle = 0; angle < 360; angle += 3) { // for every ray
let xv = Math.sin(angle*(Math.PI/180))*5;
let yv = Math.cos(angle*(Math.PI/180))*5;
let x = mouseX;
let y = mouseY;
let rayinitx = x;
let rayinity = y;
let collisions = 0;
let opacity = 1;
while (x > 0 && x < canvas.width && y > 0 && y < canvas.height) { // for every movement of every ray
x += xv;
data = ctx.getImageData(x, y, 1, 1);
let collide = false;
if (data.getPixel(0, 0, 0) === 10 && data.getPixel(0, 0, 1) === 10 && data.getPixel(0, 0, 2) === 10 && data.getPixel(0, 0, 3) === 255) {
xv *= -1;
x += xv;
collide = true;
};
y += yv;
data = ctx.getImageData(x, y, 1, 1);
if (data.getPixel(0, 0, 0) === 10 && data.getPixel(0, 0, 1) == 10 && data.getPixel(0, 0, 2) === 10 && data.getPixel(0, 0, 3) === 255) {
yv *= -1;
y += yv;
collide = true;
};
if (collide) {
rays.push([[rayinitx, rayinity], [x, y], opacity]);
opacity -= 1/5;
rayinitx = x;
rayinity = y;
if (opacity <= 0) {
break;
};
};
};
rays.push([[rayinitx, rayinity], [x, y], opacity]);
rayinitx = x;
rayinity = y;
};
rays.forEach(ray => {
ctx.strokeStyle = `rgba(255, 255, 0, ${ray[2]})`;
drawLine(ray[0], ray[1]);
});
};
document.body.addEventListener("keypress", function(e){
if (e.code == "Space") render();
});
* {
box-sizing: border-box;
}
html,body {
width: 100%;
height: 100%;
}
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 100vw;
height: 100vh;
}
<!DOCTYPE html>
<html lang="en">
<body onmousemove="mousemove(event);">
<canvas id="canvas"></canvas>
</body>
</html>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
var mouseX = 0;
var mouseY = 0;
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx.fillStyle = "rgb(10, 10, 10)";
ctx.strokeStyle = "rgba(255, 255, 0)";
ctx.lineWidth = 3;
ImageData.prototype.getPixel = function(x, y, offset) {
return this.data[(y * this.width + x) * 4 + offset];
};
function drawLine(first, second) {
ctx.beginPath();
ctx.moveTo(first[0], first[1]);
ctx.lineTo(second[0], second[1]);
ctx.stroke();
};
function mousemove(e) {
mouseX = e.clientX;
mouseY = e.clientY;
};
function drawScene() {
ctx.fillStyle = "rgba(50, 50, 50, 1)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgba(10, 10, 10, 1)";
ctx.fillRect(0, canvas.height-10, 200, 10);
ctx.fillRect(0, canvas.height-210, 10, 200);
ctx.fillRect(10, canvas.height-210, 200, 10);
ctx.fillRect(200, canvas.height-190, 10, canvas.height-190);
};
drawScene();
function render() {
drawScene();
let rays = [];
var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (let angle = 0; angle < 360; angle += 3) { // for every ray
let xv = Math.sin(angle*(Math.PI/180))*5;
let yv = Math.cos(angle*(Math.PI/180))*5;
let x = mouseX;
let y = mouseY;
let rayinitx = x;
let rayinity = y;
let collisions = 0;
let opacity = 1;
while (x > 0 && x < canvas.width && y > 0 && y < canvas.height) { // for every movement of every ray
x += xv;
let collide = false;
if (data.getPixel(x, y, 0) === 10 && data.getPixel(x, y, 1) === 10 && data.getPixel(x, y, 2) === 10 && data.getPixel(x, y, 3) === 255) {
xv *= -1;
x += xv;
collide = true;
};
y += yv;
if (data.getPixel(x, y, 0) === 10 && data.getPixel(x, y, 1) == 10 && data.getPixel(x, y, 2) === 10 && data.getPixel(x, y, 3) === 255) {
yv *= -1;
y += yv;
collide = true;
};
if (collide) {
rays.push([[rayinitx, rayinity], [x, y], opacity]);
opacity -= 1/5;
rayinitx = x;
rayinity = y;
if (opacity <= 0) {
break;
};
};
};
rays.push([[rayinitx, rayinity], [x, y], opacity]);
rayinitx = x;
rayinity = y;
};
rays.forEach(ray => {
ctx.strokeStyle = `rgba(255, 255, 0, ${ray[2]})`;
drawLine(ray[0], ray[1]);
});
};
document.body.addEventListener("keypress", function(e){
if (e.code == "Space") render();
});
* {
box-sizing: border-box;
}
html,body {
width: 100%;
height: 100%;
}
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 100vw;
height: 100vh;
}
<!DOCTYPE html>
<html lang="en">
<body onmousemove="mousemove(event);">
<canvas id="canvas"></canvas>
</body>
</html>
The problem is that your
xandyvalues are not integers. This was working ok withgetImageData()because this function is defined as accepting integers (precisely[EnforceRange] long) and that the browser will do the conversion automagically when passed floats. HoweverArrayindex access doesn't do that conversion, so when you try to access the e.g1.3424123124item of theImageData's.data, you getundefined, which is not=== 10.You can fix that by enforcing the conversion to integer in your
getPixelmethod: