// Gate evaluation and port geometry import { GATE_W, GATE_H, PORT_R, gateInputCount, gateOutputCount } from './constants.js'; import { state } from './state.js'; import { recordSample, setEvaluateAll } from './waveform.js'; export function getInputPorts(gate) { const count = gateInputCount(gate.type); const ports = []; for (let i = 0; i < count; i++) { const spacing = GATE_H / (count + 1); ports.push({ x: gate.x, y: gate.y + spacing * (i + 1), index: i, type: 'input' }); } return ports; } export function getOutputPorts(gate) { const count = gateOutputCount(gate.type); const ports = []; for (let i = 0; i < count; i++) { ports.push({ x: gate.x + GATE_W, y: gate.y + GATE_H / 2, index: i, type: 'output' }); } return ports; } export function evaluate(gate, visited = new Set()) { if (visited.has(gate.id)) return gate.value || 0; visited.add(gate.id); if (gate.type === 'INPUT' || gate.type === 'CLOCK') return gate.value; const inputCount = gateInputCount(gate.type); const inputs = []; for (let i = 0; i < inputCount; i++) { const conn = state.connections.find(c => c.to === gate.id && c.toPort === i); if (conn) { const srcGate = state.gates.find(g => g.id === conn.from); inputs.push(srcGate ? evaluate(srcGate, visited) : 0); } else { inputs.push(0); } } let result = 0; switch (gate.type) { case 'AND': result = (inputs[0] && inputs[1]) ? 1 : 0; break; case 'OR': result = (inputs[0] || inputs[1]) ? 1 : 0; break; case 'NOT': result = inputs[0] ? 0 : 1; break; case 'NAND': result = (inputs[0] && inputs[1]) ? 0 : 1; break; case 'NOR': result = (inputs[0] || inputs[1]) ? 0 : 1; break; case 'XOR': result = (inputs[0] !== inputs[1]) ? 1 : 0; break; case 'OUTPUT': result = inputs[0] || 0; break; } gate.value = result; return result; } export function evaluateAll(recordWave = false) { state.gates.forEach(g => { if (g.type !== 'INPUT' && g.type !== 'CLOCK') g.value = 0; }); state.gates.forEach(g => evaluate(g)); if (recordWave && state.recording && state.waveformVisible) recordSample(); } // Register evaluateAll in waveform to break circular dependency setEvaluateAll(evaluateAll); export function findGateAt(x, y) { return state.gates.find(g => x >= g.x && x <= g.x + GATE_W && y >= g.y && y <= g.y + GATE_H); } export function findPortAt(x, y) { for (const gate of state.gates) { for (const p of getInputPorts(gate)) { if (Math.hypot(x - p.x, y - p.y) < PORT_R + 4) return { gate, index: p.index, type: 'input' }; } for (const p of getOutputPorts(gate)) { if (Math.hypot(x - p.x, y - p.y) < PORT_R + 4) return { gate, index: p.index, type: 'output' }; } } return null; }