I don't know. I need to make this grid snapping to function properre, but it intersects between its own cells on each animationframe update, which is not intended. there should be a new wireframe line going from an attachment point or snap point and connected to actual wireframe model, but not the grid itself.  just tell what need to replace and where and what to modify with what.: <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Visual Matrix Constructor</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
    canvas {
      display: block;
    }
    .vmc-menu {
      display: none;
      position: absolute;
      background-color: rgba(0,0,0,0.1);
      border-radius: 5px;
      padding: 10px;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
      z-index:1;
    }
    .vmc-menu label {
      display: block;
      margin-bottom: 10px;
    }
    .red-dot {
      position: absolute;
      width: 5px;
      height: 5px;
      background-color: red;
      border-radius: 50%;
      pointer-events: none;
      color:red;
      z-index:0;

    }
    .green-dot {
      position: absolute;
      width: 5px;
      height: 5px;
      background-color: lime;
      border-radius: 50%;
      pointer-events: none;
      z-index:0;
    }
  </style>
</head>
<body>
  <div class="vmc-menu" id="vmc-menu">
    <label>Vertex X: <input type="number" id="vmc-vertex-x" step="0.1"></label>
    <label>Vertex Y: <input type="number" id="vmc-vertex-y" step="0.1"></label>
    <label>Vertex Z: <input type="number" id="vmc-vertex-z" step="0.1"></label>
    <button id="add-edge">Add Edge</button>
    <button id="remove-edge">Remove Edge</button>
    <button id="toggle-grid">Toggle Grid</button>
  </div>
  <div class="red-dot" id="red-dot"></div>
  <div class="green-dot" id="green-dot"></div>
<script>
  const canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);

const ctx = canvas.getContext('2d');
const vmcMenu = document.getElementById('vmc-menu');

const vertices = [
  [0, 0, 0],
  [0, 1, 0],
  [1, 1, 0],
  [1, 0, 0],
  [0, 0, 1],
  [0, 1, 1],
  [1, 1, 1],
  [1, 0, 1],
];

const edges = [
  [0, 1],
  [1, 2],
  [2, 3],
  [3, 0],
  [0, 4],
  [1, 5],
  [2, 6],
  [3, 7],
  [4, 5],
  [5, 6],
  [6, 7],
  [7, 4],
];

const scale = 0.025;
const zoom = 1;
const offsetX = 0.5;
const offsetY = 0.5;
let edgeStart = null;
let angleX = 0;
let angleY = 0;
let angleZ = 0;
  let bestIndex = -1;
  let bestDistance = Infinity;
let startNewEdgeIndex = -1;
let isMouseDown = false;
let prevMousePos = null;
    const gridSpacing = 50; 
    const gridPoints = []; 
    let displayGrid = true; 
    function roundToGrid(value) {
      return Math.round(value / gridSpacing) * gridSpacing;
    }
    
    function snapToGrid(x, y) {
      return {
        x: roundToGrid(x - canvas.getBoundingClientRect().left),
        y: roundToGrid(y - canvas.getBoundingClientRect().top)
      };
    }
// Red Dot
const redDot = document.getElementById('red-dot');
const greenDotContainer = document.getElementById('green-dot');
    document.body.appendChild(greenDotContainer);


    function addEdge(event) {
      if (bestIndex === -1) return;
      if (startNewEdgeIndex === -1) {
        const snapped = snapToGrid(window.event.clientX, window.event.clientY);
        const { x, y } = reverseProject([snapped.x, snapped.y], canvas.height * scale, offsetX, offsetY, zoom);
        vertices.push([x, y, 0]);
        startNewEdgeIndex = vertices.length - 1;
      } else {
        edges.push([startNewEdgeIndex, bestIndex]);
        startNewEdgeIndex = -1;
      }
    }

    function reverseProject(coordinates, scale, offsetX, offsetY, zoom) {
      const [x, y] = coordinates;
      return [
        (x - canvas.width / 2) / (zoom * scale) + offsetX,
        (y - canvas.height / 2) / (zoom * scale) + offsetY,
      ];
    }

// Remove Edge
document.getElementById('remove-edge').addEventListener('click', () => {
  if (bestIndex === -1) return;
  edges.forEach((edge, index) => {
    if (edge.includes(bestIndex)) {
      edges.splice(index, 1);
    }
  });
});

function rotateX(angle) {
  const c = Math.cos(angle);
  const s = Math.sin(angle);
  return [
    [1, 0, 0],
    [0, c, -s],
    [0, s, c],
  ];
}

function rotateY(angle) {
  const c = Math.cos(angle);
  const s = Math.sin(angle);
  return [
    [c, 0, s],
    [0, 1, 0],
    [-s, 0, c],
  ];
}

function rotateZ(angle) {
  const c = Math.cos(angle);
  const s = Math.sin(angle);
  return [
    [c, -s, 0],
    [s, c, 0],
    [0, 0, 1],
  ];
}

function project(vertex, scale, offsetX, offsetY, zoom) {
  const [x, y, z] = vertex;
  const posX = (x - offsetX) * scale;
  const posY = (y - offsetY) * scale;
  const posZ = z * scale;

  return [
    (posX * (zoom + posZ) + canvas.width / 2),
    (posY * (zoom + posZ) + canvas.height / 2),
  ];
}

function transform(vertex, rotationMatrix) {
  const [x, y, z] = vertex;
  const [rowX, rowY, rowZ] = rotationMatrix;
  return [
    x * rowX[0] + y * rowX[1] + z * rowX[2],
    x * rowY[0] + y * rowY[1] + z * rowY[2],
    x * rowZ[0] + y * rowZ[1] + z * rowZ[2],
  ];
}

function extraterrestrialTransformation(vertex, frequency, amplitude) {
  const [x, y, z] = vertex;
  const cosX = (Math.cos(x * frequency) * amplitude);
  const cosY = (Math.cos(y * frequency) * amplitude);
  const cosZ = (Math.cos(z * frequency) * amplitude);

  return [x + cosX, y + cosY, z + cosZ];
}

function getDeviation(maxDeviation) {
const t = Date.now() / 1000;
const frequency = 100 / 50;
const amplitude = maxDeviation / 10;
const deviation = Math.sin(t * frequency) * amplitude;
return deviation.toFixed(3);
}

function render() {
  ctx.fillStyle = '#FFF';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  const rotX = rotateX(angleX);
  const rotY = rotateY(angleY);
  const rotZ = rotateZ(angleZ);

  const transformedVertices = vertices.map(vertex => {
    const cx = vertex[0] - offsetX;
    const cy = vertex[1] - offsetY;
    const cz = vertex[2] - offsetY;

    const rotated = transform(transform(transform([cx, cy, cz], rotX), rotY), rotZ);

    return [
      rotated[0] + offsetX,
      rotated[1] + offsetY,
      rotated[2] + offsetY,
    ];
  });

  const projectedVertices = transformedVertices.map(vertex => project(vertex, canvas.height * scale, offsetX, offsetY, zoom));

  ctx.lineWidth = 2;
  ctx.strokeStyle = 'hsla(' + (angleX + offsetX + angleY + offsetY) * 55 + ', 100%, 30%, 0.8)';
  ctx.beginPath();

  for (let edge of edges) {
    const [a, b] = edge;
    const [x1, y1] = projectedVertices[a];
    const [x2, y2] = projectedVertices[b];

    const dist = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2 + (y2 - x1) ** 2 + (x2 - y1));
    const angle = Math.atan2(y2 - y1, x2 - x1, x2 - y1, y2 - x1);

    const cpDist = 0.005 * dist;
    const cpX = (x1 + x2) / 2 + cpDist * Math.cos(angle - Math.PI / 2) * getDeviation(0.2);
    const cpY = (y1 + y2) / 2 + cpDist * Math.sin(angle - Math.PI / 2) * getDeviation(0.2);

    ctx.moveTo(x1, y1, x2, y2);
    ctx.quadraticCurveTo(cpX, cpY, x2, y2, x1, y1);
  }

  ctx.stroke();

canvas.addEventListener('mousedown', (event) => {
  isMouseDown = true;
  prevMousePos = { x: event.clientX, y: event.clientY };

  edgeStart = redDot.style.display === 'block' ? { x: parseFloat(redDot.style.left) + 3, y: parseFloat(redDot.style.top) + 3 } : null;
});

canvas.addEventListener('mouseup', (event) => {
  if (edgeStart && redDot.style.display === 'block') {
    const edgeEnd = { x: parseFloat(redDot.style.left) + 3, y: parseFloat(redDot.style.top) + 3 };

    const startX = (edgeStart.x - canvas.width / 2) / (canvas.height * scale) + offsetX;
    const startY = (edgeStart.y - canvas.height / 2) / (canvas.height * scale) + offsetY;
    const startZ = 0;

    const endX = (edgeEnd.x - canvas.width / 2) / (canvas.height * scale) + offsetX;
    const endY = (edgeEnd.y - canvas.height / 2) / (canvas.height * scale) + offsetY;
    const endZ = 0;

    vertices.push([startX.toPrecision(4), startY.toPrecision(4), startZ], [endX.toPrecision(4), endY.toPrecision(4), endZ]);
    edges.push([vertices.length - 2, vertices.length - 1]);
  }
});

  canvas.addEventListener('mousemove', (event) => {
    const mousePos = {
      x: event.clientX - canvas.getBoundingClientRect().left,
      y: event.clientY - canvas.getBoundingClientRect().top
    };

    bestIndex = -1;
    bestDistance = Infinity;

    projectedVertices.forEach((currVertex, index) => {
      const distance = Math.hypot(
        currVertex[0] - mousePos.x,
        currVertex[1] - mousePos.y
      );
      if (distance < bestDistance) {
        bestIndex = index;
        bestDistance = distance;
      }
    });

    const snapMousePos = snapToGrid(mousePos.x, mousePos.y);

    if (bestDistance < gridSpacing / 2 && bestIndex !== -1) {
      const snappedVert = snapToGrid(projectedVertices[bestIndex][0], projectedVertices[bestIndex][1]);
      projectedVertices[bestIndex][0] = snappedVert.x;
      projectedVertices[bestIndex][1] = snappedVert.y;

      vmcMenu.style.display = 'block';
      vmcMenu.style.left = mousePos.x + 'px';
      vmcMenu.style.top = mousePos.y + 'px';

      document.getElementById('vmc-vertex-x').value = vertices[bestIndex][0];
      document.getElementById('vmc-vertex-y').value = vertices[bestIndex][1];
      document.getElementById('vmc-vertex-z').value = vertices[bestIndex][2];

      document.getElementById('vmc-vertex-x').dataset.vertexIndex = bestIndex;
      document.getElementById('vmc-vertex-y').dataset.vertexIndex = bestIndex;
      document.getElementById('vmc-vertex-z').dataset.vertexIndex = bestIndex;

      redDot.style.display = 'block';
      redDot.style.left = snapMousePos.x - 3 + 'px';
      redDot.style.top = snapMousePos.y - 3 + 'px';
    } else {
      vmcMenu.style.display = 'none';
      redDot.style.display = 'none';
    }

greenDotContainer.style.transform = "translate(" + (snapMousePos.x - 3) + "px, " + (snapMousePos.y - 3) + "px)";

    if (isMouseDown && prevMousePos) {
      const deltaX = event.clientX - prevMousePos.x;
      const deltaY = event.clientY - prevMousePos.y;

      angleY += deltaX * 0.01;
      angleX += deltaY * 0.01;

      prevMousePos = { x: event.clientX, y: event.clientY };
    }
  });

function updateVertexValue(event, indexToUpdate) {
  const newValue = parseFloat(event.target.value);
  const vertexIndex = parseInt(event.target.dataset.vertexIndex);

  if (!isNaN(newValue) && vertexIndex >= 0) {
    const updatedValue = indexToUpdate === 2 ? newValue : roundToGrid((newValue - 0.5) / scale) * scale + 0.5;
    vertices[vertexIndex][indexToUpdate] = updatedValue;
  }
}

document.getElementById('vmc-vertex-x').addEventListener('input', (event) => {
  updateVertexValue(event, 0);
});
document.getElementById('vmc-vertex-y').addEventListener('input', (event) => {
  updateVertexValue(event, 1);
});
document.getElementById('vmc-vertex-z').addEventListener('input', (event) => {
  updateVertexValue(event, 2);
});

angleX += +getDeviation(0.0005);
angleY += +getDeviation(0.0005);
angleZ += +getDeviation(0.0005);
  
  


    for (let i = 0; i < canvas.width; i += gridSpacing) {
      for (let j = 0; j < canvas.height; j += gridSpacing) {
        gridPoints.push({ x: i, y: j });
        const dot = document.createElement('div');
        dot.className = 'green-dot';
        dot.style.left = (i - 2) + 'px';
        dot.style.top = (j - 2) + 'px';
        dot.style.display = displayGrid ? '' : 'none';
        greenDotContainer.appendChild(dot);
      }
    }

    function renderGrid() {
      if (!displayGrid) return;
      ctx.strokeStyle = '#0F0';
      ctx.lineWidth = 1;
      for (let i = 0; i <= canvas.width; i += gridSpacing) {
        ctx.beginPath();
        ctx.moveTo(i, 0);
        ctx.lineTo(i, canvas.height);
        ctx.stroke();
      }
      for (let i = 0; i <= canvas.height; i += gridSpacing) {
        ctx.beginPath();
        ctx.moveTo(0, i);
        ctx.lineTo(canvas.width, i);
        ctx.stroke();
      }
    }

    const gridToggleButton = document.getElementById('toggle-grid');
    gridToggleButton.innerText = 'Toggle Grid';

    gridToggleButton.addEventListener('click', () => {
      displayGrid = !displayGrid;
      if (displayGrid) {
        greenDotContainer.style.display = 'block';
      } else {
        greenDotContainer.style.display = 'none';
      }
    });
renderGrid();
  requestAnimationFrame(render);
  
}



requestAnimationFrame(render);

window.addEventListener("resize", () => {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
});
  </script>
</body>
</html>