refactor: modularize into ES6 modules
Split monolithic index.html into: - js/constants.js - gate config, colors, dimensions - js/state.js - shared application state - js/gates.js - evaluation logic, port geometry - js/renderer.js - canvas drawing - js/waveform.js - GTKWave-style signal viewer - js/simulation.js - clock tick engine - js/events.js - mouse, keyboard, UI handlers - js/app.js - entry point - css/style.css - all styles
This commit is contained in:
78
js/gates.js
Normal file
78
js/gates.js
Normal file
@@ -0,0 +1,78 @@
|
||||
// Gate evaluation and port geometry
|
||||
import { GATE_W, GATE_H, PORT_R, gateInputCount, gateOutputCount } from './constants.js';
|
||||
import { state } from './state.js';
|
||||
import { recordSample } 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() {
|
||||
state.gates.forEach(g => {
|
||||
if (g.type !== 'INPUT' && g.type !== 'CLOCK') g.value = 0;
|
||||
});
|
||||
state.gates.forEach(g => evaluate(g));
|
||||
if (state.recording && state.waveformVisible) recordSample();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user