need to rework the entire code to be able to highlight the lines in 3d wireframe matrix and dragability from one end, so you can drag them from one point and attach to any other line, and if multiple lines is detached it should keep them intact (in their original shape) until atached to any other line. not sure how you do this, but you should do this. try to use a " snap-to-grid functionality" output full remodified code.: const canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);

const ctx = canvas.getContext('2d');
// some 3dmatrix simple cube template here to start with
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;

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];
}

let angleX = 0;
let angleY = 0;
let angleZ = 0;

function getDeviation(maxDeviation) {
const t = Date.now() / 1000;
const frequency = 100 / 5;
const amplitude = maxDeviation / 0.5;
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 frequency = 1;
  const amplitude = 0.8;

  const transformedVertices = vertices.map(vertex => {
    const extraterrestrialVertex = extraterrestrialTransformation(vertex, frequency, amplitude);
    const cx = extraterrestrialVertex[0] - offsetX;
    const cy = extraterrestrialVertex[1] - offsetY;
    const cz = extraterrestrialVertex[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 + angleY) * 100 + ', 100%, 50%, 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.0001 * dist;
const cpX = (x1 + x2) / 2 + cpDist * Math.cos(angle - Math.PI / 2) * getDeviation(2);
const cpY = (y1 + y2) / 2 + cpDist * Math.sin(angle - Math.PI / 2) * getDeviation(2);

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

ctx.stroke();

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

  requestAnimationFrame(render);
}

requestAnimationFrame(render);
window.addEventListener("resize", () => {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
}); here's some guidelines for you to start from: "First, we can add a mousedown event listener to the canvas to detect when the user clicks on a line. We can store the currently selected line’s indices and the position of the mouse relative to the line for later use:

let selectedLine = null; // indices of currently selected line
let clickOffset = null; // offset of click position from line start
canvas.addEventListener(‘mousedown’, (event) => {
const [x, y] = [event.clientX, event.clientY];

// Find the closest line to the click position
let minDistance = Infinity;
for (let i = 0; i < edges.length; i++) {
const [v1, v2] = edges[i];
const [x1, y1] = projectedVertices[v1];
const [x2, y2] = projectedVertices[v2];
const distance = getDistanceFromPointToLine(x, y, x1, y1, x2, y2);
if (distance < minDistance) {
minDistance = distance;
selectedLine = [v1, v2];
clickOffset = [x - x1, y - y1];
}
}
});

Next, we can add a mousemove event listener to update the position of the selected line based on the mouse movement:

canvas.addEventListener(‘mousemove’, (event) => {
if (selectedLine !== null) {
const [x, y] = [event.clientX, event.clientY];
const [v1, v2] = selectedLine;
const [x1, y1] = [x - clickOffset[0], y - clickOffset[1]];
projectedVertices[v1] = [x1, y1];
}
});

Finally, we can add a mouseup event listener to clear the selected line when the user releases the mouse button:

canvas.addEventListener(‘mouseup’, (event) => {
selectedLine = null;
});"