// Great resource from https://stackoverflow.com/a/40700068 // Thank you ConnorFan var strokeWidth = 10; var bufferSize; var svgElement = document.getElementById("svgElement"); var rect = svgElement.getBoundingClientRect(); var path = null; var strPath; var buffer = []; // Contains the last positions of the mouse cursor var cable; const logs = document.getElementById("logs"); class Cable { start = ""; end = ""; path = ""; color = ""; constructor(start) { this.start = start; } } svgElement.addEventListener("mousedown", function (e) { bufferSize = document.getElementById("cmbBufferSize").value; path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("fill", "none"); let color = randomColor(); path.setAttribute("stroke", color); path.setAttribute("stroke-width", strokeWidth); path.setAttribute("stroke-linecap", "round"); buffer = []; var pt = getMousePosition(e); appendToBuffer(pt); strPath = "M" + pt.x + " " + pt.y; path.setAttribute("d", strPath); svgElement.appendChild(path); let socket = getSocket(pt); if (socket) { cable = new Cable(socket.querySelector("input").getAttribute("name")); cable.color = color; } }); svgElement.addEventListener("mousemove", function (e) { if (path) { appendToBuffer(getMousePosition(e)); updateSvgPath(); } }); svgElement.addEventListener("mouseup", function (e) { if (path) { let pt = getMousePosition(e); let socket = getSocket(pt); if (socket) { cable.end = socket.querySelector("input").getAttribute("name"); cable.path = path.getAttribute("d"); logCable(cable); } path = null; } }); svgElement.addEventListener("mouseleave", function (e) { if (path) { let pt = getMousePosition(e); let socket = getSocket(pt); if (socket) { cable.end = socket.querySelector("input").getAttribute("name"); cable.path = path.getAttribute("d"); logCable(cable); } path = null; } }); // ok var getMousePosition = function (e) { return { x: e.pageX - rect.left, y: e.pageY - rect.top, }; }; var appendToBuffer = function (pt) { buffer.push(pt); while (buffer.length > bufferSize) { buffer.shift(); } }; // ok // Calculate the average point, starting at offset in the buffer var getAveragePoint = function (offset) { var len = buffer.length; if (len % 2 === 1 || len >= bufferSize) { var totalX = 0; var totalY = 0; var pt, i; var count = 0; for (i = offset; i < len; i++) { count++; pt = buffer[i]; totalX += pt.x; totalY += pt.y; } return { x: totalX / count, y: totalY / count, }; } return null; }; // ok var updateSvgPath = function () { var pt = getAveragePoint(0); if (pt) { // Get the smoothed part of the path that will not change strPath += " L" + pt.x + " " + pt.y; // Get the last part of the path (close to the current mouse position) // This part will change if the mouse moves again var tmpPath = ""; for (var offset = 2; offset < buffer.length; offset += 2) { pt = getAveragePoint(offset); tmpPath += " L" + pt.x + " " + pt.y; } // Set the complete current path coordinates path.setAttribute("d", strPath + tmpPath); } }; // ok var randomColor = function () { const hue = Math.floor(Math.random() * 360); const saturation = Math.floor(50 + Math.random() * (50 + 1)) + "%"; const lightness = "75%"; return "hsl(" + hue + ", " + saturation + ", " + lightness + ")"; }; // ok var getSocket = function (position) { precision = 8; size = 24; const sockets = document.querySelectorAll(".socket"); sock = Array.from(sockets).filter((socket) => { input = socket.querySelector("input"); return ( position.x >= input.offsetLeft - precision + size / 2 && position.x <= input.offsetLeft + precision + size / 2 && position.y >= input.offsetTop - precision + size / 2 && position.y <= input.offsetTop + precision + size / 2 ); })[0]; return sock; }; let logCable = function (cable) { let entry = document.createElement("li"); let marker = document.createElement("span"); marker.classList.add("marker"); marker.style.backgroundColor = cable.color; entry.appendChild(marker); let text = document.createElement("span"); text.innerHTML = `connects ${cable.start} with ${cable.end}`; entry.appendChild(text); logs.appendChild(entry); };