need to make this zoom function to zoom evenly as it goes close to the center of that 3d matrix grid, because it getting slower and slower as you zooming close to the center. also, need to apply some z-index factor for these 3d matrix grid points, so they can get smaller in dependence of how far they are in z-index or "z-buffer". also, it will be nice to make some simple round gradient for these grid points too look more volumeric in general for 3d matrix grid structure. output full javascript.: const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

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

const rotationSpeed = 0;
const gridSize = 50;
const gridSpacing = 25;
const zoomStep = 10;

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;
let zoom = 800;
let dragStart = null;

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 = zoom / (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;
  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("wheel", (e) => {
  e.preventDefault();
  zoom += e.deltaY * -0.1 * zoomStep;
  zoom = Math.max(100, zoom); // Set a minimum zoom limit
});

canvas.addEventListener("mousedown", (e) => {
  dragStart = { x: e.clientX, y: e.clientY };
});

canvas.addEventListener("mousemove", (e) => {
  if (dragStart) {
    const deltaX = e.clientX - dragStart.x;
    const deltaY = e.clientY - dragStart.y;
    angleX += deltaY * 0.01;
    angleY += deltaX * 0.01;
    dragStart = { x: e.clientX, y: e.clientY };
  }
});

canvas.addEventListener("mouseup", () => {
  dragStart = null;
});

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();