do zoom ability here, to zoom in and out to the center of that cube grid with mouse wheel with some adjustable precise step size in zooming. also, do some rotation on all axis by mouse dragging in empty space, set flag for auto-rotation to false. also, auto-fit the entire 3d matrix grid to full canvas automatically. need to preserve that clicking functionality to draw lines on grid. integrate all, output only full javascript code, without html code included.:
<html lang=“en”>
<head>
<meta charset=“UTF-8”>
<title>drawacube</title>
<style>
canvas {
display: block;
margin: auto;
}
</style>
</head>
<body>
<canvas id=“canvas”></canvas>
<script>
const canvas = document.getElementById(“canvas”);
const ctx = canvas.getContext(“2d”);

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const rotationSpeed = 0.001;
const gridSize = 50;
const gridSpacing = 25;

let grid = [];
for (let x = -gridSize; x <= gridSize; x += gridSpacing) {
for (let y = -gridSize; y <= gridSize; y += gridSpacing) {
for (let z = -gridSize; z <= gridSize; z += gridSpacing) {
grid.push({ x, y, z });
}
}
}
let lastClickedPoint = null;
const clickedPoints = [];
let angleX = 0;
let angleY = 0;

function rotate(point, angleX, angleY) {
const { x, y, z } = point;
const cosX = Math.cos(angleX);
const sinX = Math.sin(angleX);
const cosY = Math.cos(angleY);
const sinY = Math.sin(angleY);

const newY = y * cosX - z * sinX;
const newZ = y * sinX + z * cosX;
const newX = x * cosY - newZ * sinY;
const newZ2 = x * sinY + newZ * cosY;

return { x: newX, y: newY, z: newZ2 };
}

function project(point, width, height) {
const { x, y, z } = point;

const scale = 800 / (400 + z);
const newX = x * scale + width / 2;
const newY = y * scale + height / 2;

return { x: newX, y: newY };
}

function findClosestPoint(point) {
const threshold = 20; // Adjust this value to set the click tolerance
let minDist = Infinity;
let minIndex = -1;

for (let i = 0; i < grid.length; i++) {
const projected = project(rotate(grid[i], angleX, angleY), canvas.width, canvas.height);
const dx = point.x - projected.x;
const dy = point.y - projected.y;
const dist = Math.sqrt(dx * dx + dy * dy);

if (dist < minDist && dist <= threshold) {
minDist = dist;
minIndex = i;
}
}

return minIndex !== -1 ? grid[minIndex] : null;
}

let mouseDown = false;
let activeLine = null;
const lines = [];

canvas.addEventListener(“mousedown”, (e) => {
mouseDown = true;
const canvasRect = canvas.getBoundingClientRect();
const mouseX = e.clientX - canvasRect.left;
const mouseY = e.clientY - canvasRect.top;
const startPoint = findClosestPoint({ x: mouseX, y: mouseY });

activeLine = [startPoint];
});
canvas.addEventListener(“mousedown”, (e) => {
mouseDown = true;
const canvasRect = canvas.getBoundingClientRect();
const mouseX = e.clientX - canvasRect.left;
const mouseY = e.clientY - canvasRect.top;
const startPoint = findClosestPoint({ x: mouseX, y: mouseY });

activeLine = [startPoint];
});

canvas.addEventListener(“mouseup”, (e) => {
mouseDown = false;

if (activeLine !== null) {
const canvasRect = canvas.getBoundingClientRect();
const mouseX = e.clientX - canvasRect.left;
const mouseY = e.clientY - canvasRect.top;
const endPoint = findClosestPoint({ x: mouseX, y: mouseY });

if (endPoint !== activeLine[activeLine.length - 1]) {
activeLine.push(endPoint);
lines.push(activeLine);
}
activeLine = null;
}

if (lastClickedPoint !== null) {
const canvasRect = canvas.getBoundingClientRect();
const mouseX = e.clientX - canvasRect.left;
const mouseY = e.clientY - canvasRect.top;
const clickedPoint = findClosestPoint({ x: mouseX, y: mouseY });

if (clickedPoint !== null) {
if (lastClickedPoint === clickedPoint) {
lastClickedPoint = null;
} else {
lines.push([lastClickedPoint, clickedPoint]);
clickedPoints.push(clickedPoint);
lastClickedPoint = null;
}
}
} else {
const canvasRect = canvas.getBoundingClientRect();
const mouseX = e.clientX - canvasRect.left;
const mouseY = e.clientY - canvasRect.top;
const clickedPoint = findClosestPoint({ x: mouseX, y: mouseY });

if (clickedPoint !== null) {
lastClickedPoint = clickedPoint;
clickedPoints.push(clickedPoint);
}
}
});
function drawLine(line) {
ctx.beginPath();

for (let i = 0; i < line.length - 1; i++) {
const projectedStart = project(rotate(line[i], angleX, angleY), canvas.width, canvas.height);
const projectedEnd = project(rotate(line[i + 1], angleX, angleY), canvas.width, canvas.height);

ctx.moveTo(projectedStart.x, projectedStart.y);
ctx.lineTo(projectedEnd.x, projectedEnd.y);
}

ctx.strokeStyle = “rgba(25,200,25,0.2)”;
ctx.lineWidth = 2;
ctx.stroke();
}

function draw() {
ctx.fillStyle = “rgba(1,2,1,0.8)”;
ctx.fillRect(0, 0, canvas.width, canvas.height);

for (const point of grid) {
const rotated = rotate(point, angleX, angleY);
const projected = project(rotated, canvas.width, canvas.height);

ctx.beginPath();
ctx.arc(projected.x, projected.y, 2, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = “rgba(55,155,255,0.8)”;
ctx.fill();
}

// Drawing clicked points in green
ctx.fillStyle = “rgba(255,200,50,0.8)”;
for (const clickedPoint of clickedPoints) {
const rotated = rotate(clickedPoint, angleX, angleY);
const projected = project(rotated, canvas.width, canvas.height);

ctx.beginPath();
ctx.arc(projected.x, projected.y, 4, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
}

for (const line of lines) {
drawLine(line);
}

if (activeLine !== null) {
drawLine(activeLine);
}

angleX += rotationSpeed;
angleY += rotationSpeed;

requestAnimationFrame(draw);
}

draw();
</script>
</body>
</html>