UNPKG

@webwriter/logic-circuit

Version:

Create and simulate circuits with logic gates (AND, OR, XOR, etc.).

283 lines (240 loc) 10.3 kB
export function getConnectorCoordinates(svgCanvas, connector, zoom) { const connectorRect = connector?.getBoundingClientRect(); const offsetX = connector?.getBoundingClientRect().width / 2; const offsetY = connector?.getBoundingClientRect().height / 2; const x = (connectorRect?.left - svgCanvas?.getBoundingClientRect()?.left + offsetX - (9*zoom)) / zoom; const y = (connectorRect?.top - svgCanvas?.getBoundingClientRect()?.top + offsetY - (9*zoom)) / zoom; return { x, y }; } export function getMouseCoordinates(svgCanvas, mouseX, mouseY, zoom) { const workspaceX = svgCanvas.getBoundingClientRect().left; const workspaceY = svgCanvas.getBoundingClientRect().top; const relativeX = mouseX - workspaceX; const relativeY = mouseY - workspaceY; const scaledRelativeX = relativeX / zoom; const scaledRelativeY = relativeY / zoom; return { x: scaledRelativeX, y: scaledRelativeY }; } export function getConnectorCoordinatesGhost(svgCanvas, connector) { const connectorRect = connector?.getBoundingClientRect(); const offsetX = connector?.getBoundingClientRect().width / 2; const offsetY = connector?.getBoundingClientRect().height / 2; const x = connectorRect?.left - svgCanvas?.getBoundingClientRect()?.left + offsetX - 9; const y = connectorRect?.top - svgCanvas?.getBoundingClientRect()?.top + offsetY - 9; return { x, y }; } export function calculatePath(svgCanvas, startCon, endCon, zoom) { let startConnector; let endConnector; if (startCon.type === 'input' && endCon.type === 'output') { startConnector = endCon; endConnector = startCon; } else if (startCon.type === 'output' && endCon.type === 'input') { startConnector = startCon; endConnector = endCon; } else { return; } const points = []; const padding = 15; const offsetX = 5; const start = { x: getConnectorCoordinates(svgCanvas, startConnector, zoom).x, y: getConnectorCoordinates(svgCanvas, startConnector, zoom).y, type: startConnector?.id?.substring(startConnector.id.length - 3), }; const end = { x: getConnectorCoordinates(svgCanvas, endConnector, zoom).x , y: getConnectorCoordinates(svgCanvas, endConnector, zoom).y, type: endConnector?.id?.substring(endConnector.id.length - 3), }; points.push({ x: start.x, y: start.y }); const isAbove = end.y < start.y; const isLeft = end.x < start.x + offsetX + padding; if (isLeft) { if (start.type === 'Out' || start.type === 'ut2' || start.type === 'ut3') { points.push({ x: start.x + padding, y: start.y }); if (end.type === 'In1' || end.type === 'In2') { points.push({ x: start.x + padding, y: (start.y + end.y) / 2 }); points.push({ x: end.x - padding, y: (start.y + end.y) / 2 }); points.push({ x: end.x - padding, y: end.y }); } } } else { if (start.type === 'Out' || start.type === 'ut2' || start.type === 'ut3') { if (isAbove) { points.push({ x: start.x + padding - offsetX, y: start.y }); if (end.type === 'In1') { points.pop(); points.push({ x: (start.x + end.x) / 2 - offsetX, y: start.y }); points.push({ x: (start.x + end.x) / 2 - offsetX, y: end.y }); } else if (end.type === 'In2') { points.pop(); points.push({ x: (start.x + end.x) / 2 + offsetX, y: start.y }); points.push({ x: (start.x + end.x) / 2 + offsetX, y: end.y }); } } else { points.push({ x: start.x + padding + offsetX, y: start.y }); if (end.type === 'In1') { points.pop(); points.push({ x: (start.x + end.x) / 2 + offsetX, y: start.y }); points.push({ x: (start.x + end.x) / 2 + offsetX, y: end.y }); } else if (end.type === 'In2') { points.pop(); points.push({ x: (start.x + end.x) / 2 - offsetX, y: start.y }); points.push({ x: (start.x + end.x) / 2 - offsetX, y: end.y }); } } } } points.push({ x: end.x, y: end.y }); return points; } export function calculatePathToMouse(svgCanvas, connector, zoom, mouseX, mouseY, x1 = -9, y1 = -9, x2 = -9, y2 = -9){ let start; let end; if (connector.type === 'input') { start = { x: mouseX, y: mouseY, type: 'Out', }; end = { x: getConnectorCoordinates(svgCanvas, connector, zoom).x, y: getConnectorCoordinates(svgCanvas, connector, zoom).y, type: connector?.id?.substring(connector.id.length - 3), }; } else if (connector.type === 'output') { start = { x: getConnectorCoordinates(svgCanvas, connector, zoom).x, y: getConnectorCoordinates(svgCanvas, connector, zoom).y, type: connector?.id?.substring(connector.id.length - 3), }; end = { x: mouseX, y: mouseY, type: 'In1', }; } else { return; } const points = []; const padding = 15; points.push({ x: start.x, y: start.y }); const isLeft = end.x < start.x; if (start.type === 'Out' || start.type === 'ut2' || start.type === 'ut3') { points.push({ x: start.x + padding, y: start.y}); if (end.type === 'In1' || end.type === 'In2') { if (start.y === end.y && isLeft) { points.push({ x: start.x + padding, y: start.y + padding }); points.push({ x: end.x - padding, y: start.y + padding }); points.push({ x: end.x - padding, y: end.y }); } else if (isLeft) { points.push({ x: start.x + padding, y: (start.y + end.y) / 2 }); points.push({ x: end.x - padding, y: (start.y + end.y) / 2 }); points.push({ x: end.x - padding, y: end.y }); } else { points.pop(); points.push({ x: (start.x + end.x) / 2, y: start.y }); points.push({ x: (start.x + end.x) / 2, y: end.y }); } } } points.push({ x: end.x, y: end.y }); return points; } export function updateLines(widget, movedGate) { const moveLines = []; widget.lineElements.forEach((lineObject) => { const startConnector = lineObject.start; const endConnector = lineObject.end; if ( startConnector.id === movedGate.conIn1?.id || startConnector.id === movedGate.conIn2?.id || startConnector.id === movedGate.conOut?.id || startConnector.id === movedGate.conOut2?.id || endConnector.id === movedGate.conIn1?.id || endConnector.id === movedGate.conIn2?.id || endConnector.id === movedGate.conOut?.id || endConnector.id === movedGate.conOut2?.id ) { moveLines.push(lineObject); } }); moveLines.forEach((line) => { const startConnector = line.start; const endConnector = line.end; const svgPath = line.lineSVG; const points = calculatePath(widget.svgCanvas, startConnector, endConnector, widget.zoom); let path = `M ${points[0].x} ${points[0].y}`; for (let i = 1; i < points.length; i++) { path += ` L ${points[i].x} ${points[i].y}`; } svgPath.setAttribute('d', path); }); } export function createLine(widget, startCon, endCon) { let startConnector; let endConnector; if (startCon.type === 'input' && endCon.type === 'output') { startConnector = endCon; endConnector = startCon; } else if (startCon.type === 'output' && endCon.type === 'input') { startConnector = startCon; endConnector = endCon; } else { return; } const points = calculatePath(widget.svgCanvas, startCon, endCon, widget.zoom); let path = `M ${points[0].x} ${points[0].y}`; for (let i = 1; i < points.length; i++) { path += ` L ${points[i].x} ${points[i].y}`; } const svgPath = document.createElementNS('http://www.w3.org/2000/svg', 'path'); svgPath.setAttribute('d', path); svgPath.setAttribute('id', 'line' + widget.lineID); svgPath.classList.add('svgLine'); widget.lineID++; const lineObject = { start: startConnector, end: endConnector, lineSVG: svgPath, }; widget.lineElements = [...widget.lineElements, lineObject]; svgPath.addEventListener('contextmenu', (event) => { event.preventDefault(); const deletePathID = svgPath.getAttribute('id'); widget.lineElements.forEach((line, index) => { if (line.lineSVG.id === deletePathID) { const deletePath = svgPath; deletePath.remove(); widget.lineElements.splice(index, 1); } }); }); widget.svgCanvas.appendChild(svgPath); console.log(widget.lineElements) let entry: string = startCon.id+"|"+endCon.id console.log(entry) if(!widget.reflectCons.includes(entry)){ widget.reflectCons+= widget.reflectCons != "" ? "," : "" widget.reflectCons+= entry } } export function updateLineColor(widget) { const lineElements = widget.getLineElements(); let svgPath; lineElements.forEach((line) => { svgPath = line.lineSVG; if (line.start.value === true || line.end.value === true) { svgPath.classList.add('svgLineTrue'); } else { svgPath.classList.remove('svgLineTrue'); } }); } export function resetLines(widget) { widget.lineElements.forEach((line) => { line.lineSVG.classList.remove('svgLineTrue'); }); }