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 x
and y
values are not integers. This was working ok with getImageData()
because this function is defined as accepting integers (precisely [EnforceRange] long
) and that the browser will do the conversion automagically when passed floats. However Array
index access doesn't do that conversion, so when you try to access the e.g 1.3424123124
item of the ImageData
's .data
, you get undefined
, which is not === 10
.
You can fix that by enforcing the conversion to integer in your getPixel
method:
ImageData.prototype.getPixel = function(x, y, offset) {
return this.data[(Math.floor(y) * this.width + Math.floor(x)) * 4 + offset];
};
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[(Math.floor(y) * this.width + Math.floor(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>