@webwriter/logic-circuit
Version:
Create and simulate circuits with logic gates (AND, OR, XOR, etc.).
318 lines (282 loc) • 11 kB
text/typescript
import LogicCircuit from '../../webwriter-logic-circuit';
import ConnectorElement from '../connector';
import { updateLines } from '../helper/line-helper';
export function addGate(widget: any, event: any, load?: string[]) {
const gateType = load != undefined ? load[0] : event.dataTransfer.getData('type');
let newGate;
if (gateType !== 'INPUT' && gateType !== 'OUTPUT' && gateType !== 'SPLITTER') {
if (!checkIfGateAllowed(widget, gateType)) {
return;
}
}
switch (gateType) {
case 'NOT':
newGate = widget.shadowRoot.createElement('not-gate');
newGate.id = 'notGate' + widget.gateID;
break;
case 'AND':
newGate = widget.shadowRoot.createElement('and-gate');
newGate.id = 'andGate' + widget.gateID;
break;
case 'OR':
newGate = widget.shadowRoot.createElement('or-gate');
newGate.id = 'orGate' + widget.gateID;
break;
case 'NAND':
newGate = widget.shadowRoot.createElement('nand-gate');
newGate.id = 'nandGate' + widget.gateID;
break;
case 'NOR':
newGate = widget.shadowRoot.createElement('nor-gate');
newGate.id = 'norGate' + widget.gateID;
break;
case 'XNOR':
newGate = widget.shadowRoot.createElement('xnor-gate');
newGate.id = 'xnorGate' + widget.gateID;
break;
case 'XOR':
newGate = widget.shadowRoot.createElement('xor-gate');
newGate.id = 'xorGate' + widget.gateID;
break;
case 'INPUT':
newGate = widget.shadowRoot.createElement('input-gate');
newGate.id = 'inputGate' + widget.gateID;
newGate.input1 = false;
break;
case 'OUTPUT':
newGate = widget.shadowRoot.createElement('output-gate');
newGate.id = 'outputGate' + widget.gateID;
break;
case 'SPLITTER':
newGate = widget.shadowRoot.createElement('splitter-gate');
newGate.id = 'splitterGate' + widget.gateID;
break;
}
newGate.style.position = 'absolute';
if(load == undefined){
var grabPosX = parseFloat(event.dataTransfer.getData('offsetX'));
var grabPosY = parseFloat(event.dataTransfer.getData('offsetY'));
var mouseX = event.clientX;
var mouseY = event.clientY;
}
const offsetX = widget.wsDrag.getBoundingClientRect().left;
const workspaceX = widget.wsDrag.getBoundingClientRect().left;
const workspaceY = widget.wsDrag.getBoundingClientRect().top;
const relativeX = mouseX - workspaceX;
const relativeY = mouseY - workspaceY;
const scaledRelativeX = relativeX / widget.zoom;
const scaledRelativeY = relativeY / widget.zoom;
newGate.style.left = load != undefined ? load[1] : scaledRelativeX - grabPosX + 'px';
newGate.style.top = load != undefined ? load[2] : scaledRelativeY - grabPosY + 'px';
widget.gateID++;
newGate.movable = true;
newGate.widget = widget
widget.wsDrag.appendChild(newGate);
widget.gateElements = [...widget.gateElements, newGate];
if(load == undefined){
widget.reflectGates+= widget.reflectGates != "" ? "," : ""
widget.reflectGates+= (widget.gateID-1)+"|"+gateType+"|"+newGate.style.left+"|"+newGate.style.top
}
}
export function moveGate(widget, event) {
const id = event.dataTransfer.getData('id');
const draggedGate = widget.gateElements.find((gate) => gate.id === id);
const grabPosX = parseFloat(event.dataTransfer.getData('offsetX')) / widget.zoom;
const grabPosY = parseFloat(event.dataTransfer.getData('offsetY')) / widget.zoom;
const mouseX = event.clientX;
const mouseY = event.clientY;
const workspaceX = widget.wsDrag.getBoundingClientRect().left;
const workspaceY = widget.wsDrag.getBoundingClientRect().top;
const relativeX = mouseX - workspaceX;
const relativeY = mouseY - workspaceY;
const scaledRelativeX = relativeX / widget.zoom;
const scaledRelativeY = relativeY / widget.zoom;
draggedGate.style.left = scaledRelativeX - grabPosX + 'px';
draggedGate.style.top = scaledRelativeY - grabPosY + 'px';
updateLines(widget, draggedGate);
let gatesArr = widget.reflectGates.split(",")
for(let i = 0; i<gatesArr.length; i++){
if(Number.parseInt(gatesArr[i][0]) === Number.parseInt(id.match(/^([a-zA-Z]+)(\d+)$/)[2])){
let arr: string[] = gatesArr[i].split("|")
arr[2] = draggedGate.style.left
arr[3] = draggedGate.style.top
gatesArr[i] = arr.toString().replaceAll(",","|")
}
};
widget.reflectGates = gatesArr.toString()
}
export function transferOutputToNextGate(widget: any, gate: any) {
const nextLineArray = widget.lineElements.filter(
(lineObject) =>
lineObject.start.id === (gate.conOut as ConnectorElement)?.id ||
lineObject.end.id === (gate.conOut as ConnectorElement)?.id ||
lineObject.start.id === (gate.conOut2 as ConnectorElement)?.id ||
lineObject.end.id === (gate.conOut2 as ConnectorElement)?.id
);
nextLineArray?.forEach((nextLine) => {
let nextConnector;
if (nextLine) {
if (
nextLine?.start.id === (gate.conOut as ConnectorElement)?.id ||
nextLine?.start.id === (gate.conOut2 as ConnectorElement)?.id
) {
nextConnector = nextLine.end;
} else if (
nextLine?.end.id === (gate.conOut as ConnectorElement)?.id ||
nextLine?.end.id === (gate.conOut2 as ConnectorElement)?.id
) {
nextConnector = nextLine.start;
}
}
if (nextConnector) {
const nextGate = widget.gateElements.find(
(gate) => gate.conIn1?.id === nextConnector.id || gate.conIn2?.id === nextConnector.id
);
if (nextGate.conIn1.id === nextConnector.id) {
nextGate.input1 = gate.output;
} else if (nextGate.conIn2.id === nextConnector.id) {
nextGate.input2 = gate.output;
}
}
});
}
export function checkIfGateAllowed(widget: any, gateType: any) {
const gatesOfSameType = widget.gateElements.filter((gate) => gate.gatetype === gateType);
switch (gateType) {
case 'NOT':
if (widget.notGateAllowed < 0) {
return true;
}
if (gatesOfSameType.length < widget.notGateAllowed) {
return true;
}
break;
case 'AND':
if (widget.andGateAllowed < 0) {
return true;
}
if (gatesOfSameType.length < widget.andGateAllowed) {
return true;
}
break;
case 'OR':
if (widget.orGateAllowed < 0) {
return true;
}
if (gatesOfSameType.length < widget.orGateAllowed) {
return true;
}
break;
case 'NAND':
if (widget.nandGateAllowed < 0) {
return true;
}
if (gatesOfSameType.length < widget.nandGateAllowed) {
return true;
}
break;
case 'NOR':
if (widget.norGateAllowed < 0) {
return true;
}
if (gatesOfSameType.length < widget.norGateAllowed) {
return true;
}
break;
case 'XNOR':
if (widget.xnorGateAllowed < 0) {
return true;
}
if (gatesOfSameType.length < widget.xnorGateAllowed) {
return true;
}
break;
case 'XOR':
if (widget.xorGateAllowed < 0) {
return true;
}
if (gatesOfSameType.length < widget.xorGateAllowed) {
return true;
}
break;
default:
break;
}
return false;
}
export function gateCounter(widget, gateType) {
const gatesOfSameType = widget.gateElements.filter((gate) => gate.gatetype === gateType);
switch (gateType) {
case 'NOT':
if (widget.notGateAllowed === -1) {
return;
}
return gatesOfSameType.length + '/' + widget.notGateAllowed;
break;
case 'AND':
if (widget.andGateAllowed === -1) {
return;
}
return gatesOfSameType.length + '/' + widget.andGateAllowed;
break;
case 'OR':
if (widget.orGateAllowed === -1) {
return;
}
return gatesOfSameType.length + '/' + widget.orGateAllowed;
break;
case 'NAND':
if (widget.nandGateAllowed === -1) {
return;
}
return gatesOfSameType.length + '/' + widget.nandGateAllowed;
break;
case 'NOR':
if (widget.norGateAllowed === -1) {
return;
}
return gatesOfSameType.length + '/' + widget.norGateAllowed;
break;
case 'XNOR':
if (widget.xnorGateAllowed === -1) {
return;
}
return gatesOfSameType.length + '/' + widget.xnorGateAllowed;
break;
case 'XOR':
if (widget.xorGateAllowed === -1) {
return;
}
return gatesOfSameType.length + '/' + widget.xorGateAllowed;
break;
default:
break;
}
}
export function isDropOverTrashIcon(widget: any, event: any) {
const trashCanRect = widget.workspaceContainer.querySelector('.trashCanIcon').getBoundingClientRect();
const mouseX = event.clientX;
const mouseY = event.clientY;
return (
mouseX >= trashCanRect.left &&
mouseX <= trashCanRect.right &&
mouseY >= trashCanRect.top &&
mouseY <= trashCanRect.bottom
);
}
export function resetGates(widget) {
widget.gateElements.forEach((gate) => {
if (gate.gatetype !== 'INPUT') {
gate.output = undefined;
gate.output2 = undefined;
gate.input1 = undefined;
gate.input2 = undefined;
}
if (gate.gatetype === 'OUTPUT') {
gate.input1 = false;
gate.output = false;
}
gate.resetConnectorColor();
gate.classList.remove('gateTrue');
});
}